Roo/LayoutRegion.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     // A FEW HACKS!!?
16524     
16525     text = text.replace(/\\\n/g,' ');
16526     return Roo.Markdown.marked(text);
16527 };
16528 //
16529 // converter
16530 //
16531 // Wraps all "globals" so that the only thing
16532 // exposed is makeHtml().
16533 //
16534 (function() {
16535     
16536     /**
16537      * Block-Level Grammar
16538      */
16539     
16540     var block = {
16541       newline: /^\n+/,
16542       code: /^( {4}[^\n]+\n*)+/,
16543       fences: noop,
16544       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16545       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16546       nptable: noop,
16547       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16548       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16549       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16550       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16551       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16552       table: noop,
16553       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16554       text: /^[^\n]+/
16555     };
16556     
16557     block.bullet = /(?:[*+-]|\d+\.)/;
16558     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16559     block.item = replace(block.item, 'gm')
16560       (/bull/g, block.bullet)
16561       ();
16562     
16563     block.list = replace(block.list)
16564       (/bull/g, block.bullet)
16565       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16566       ('def', '\\n+(?=' + block.def.source + ')')
16567       ();
16568     
16569     block.blockquote = replace(block.blockquote)
16570       ('def', block.def)
16571       ();
16572     
16573     block._tag = '(?!(?:'
16574       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16575       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16576       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16577     
16578     block.html = replace(block.html)
16579       ('comment', /<!--[\s\S]*?-->/)
16580       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16581       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16582       (/tag/g, block._tag)
16583       ();
16584     
16585     block.paragraph = replace(block.paragraph)
16586       ('hr', block.hr)
16587       ('heading', block.heading)
16588       ('lheading', block.lheading)
16589       ('blockquote', block.blockquote)
16590       ('tag', '<' + block._tag)
16591       ('def', block.def)
16592       ();
16593     
16594     /**
16595      * Normal Block Grammar
16596      */
16597     
16598     block.normal = merge({}, block);
16599     
16600     /**
16601      * GFM Block Grammar
16602      */
16603     
16604     block.gfm = merge({}, block.normal, {
16605       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16606       paragraph: /^/,
16607       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16608     });
16609     
16610     block.gfm.paragraph = replace(block.paragraph)
16611       ('(?!', '(?!'
16612         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16613         + block.list.source.replace('\\1', '\\3') + '|')
16614       ();
16615     
16616     /**
16617      * GFM + Tables Block Grammar
16618      */
16619     
16620     block.tables = merge({}, block.gfm, {
16621       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16622       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16623     });
16624     
16625     /**
16626      * Block Lexer
16627      */
16628     
16629     function Lexer(options) {
16630       this.tokens = [];
16631       this.tokens.links = {};
16632       this.options = options || marked.defaults;
16633       this.rules = block.normal;
16634     
16635       if (this.options.gfm) {
16636         if (this.options.tables) {
16637           this.rules = block.tables;
16638         } else {
16639           this.rules = block.gfm;
16640         }
16641       }
16642     }
16643     
16644     /**
16645      * Expose Block Rules
16646      */
16647     
16648     Lexer.rules = block;
16649     
16650     /**
16651      * Static Lex Method
16652      */
16653     
16654     Lexer.lex = function(src, options) {
16655       var lexer = new Lexer(options);
16656       return lexer.lex(src);
16657     };
16658     
16659     /**
16660      * Preprocessing
16661      */
16662     
16663     Lexer.prototype.lex = function(src) {
16664       src = src
16665         .replace(/\r\n|\r/g, '\n')
16666         .replace(/\t/g, '    ')
16667         .replace(/\u00a0/g, ' ')
16668         .replace(/\u2424/g, '\n');
16669     
16670       return this.token(src, true);
16671     };
16672     
16673     /**
16674      * Lexing
16675      */
16676     
16677     Lexer.prototype.token = function(src, top, bq) {
16678       var src = src.replace(/^ +$/gm, '')
16679         , next
16680         , loose
16681         , cap
16682         , bull
16683         , b
16684         , item
16685         , space
16686         , i
16687         , l;
16688     
16689       while (src) {
16690         // newline
16691         if (cap = this.rules.newline.exec(src)) {
16692           src = src.substring(cap[0].length);
16693           if (cap[0].length > 1) {
16694             this.tokens.push({
16695               type: 'space'
16696             });
16697           }
16698         }
16699     
16700         // code
16701         if (cap = this.rules.code.exec(src)) {
16702           src = src.substring(cap[0].length);
16703           cap = cap[0].replace(/^ {4}/gm, '');
16704           this.tokens.push({
16705             type: 'code',
16706             text: !this.options.pedantic
16707               ? cap.replace(/\n+$/, '')
16708               : cap
16709           });
16710           continue;
16711         }
16712     
16713         // fences (gfm)
16714         if (cap = this.rules.fences.exec(src)) {
16715           src = src.substring(cap[0].length);
16716           this.tokens.push({
16717             type: 'code',
16718             lang: cap[2],
16719             text: cap[3] || ''
16720           });
16721           continue;
16722         }
16723     
16724         // heading
16725         if (cap = this.rules.heading.exec(src)) {
16726           src = src.substring(cap[0].length);
16727           this.tokens.push({
16728             type: 'heading',
16729             depth: cap[1].length,
16730             text: cap[2]
16731           });
16732           continue;
16733         }
16734     
16735         // table no leading pipe (gfm)
16736         if (top && (cap = this.rules.nptable.exec(src))) {
16737           src = src.substring(cap[0].length);
16738     
16739           item = {
16740             type: 'table',
16741             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16742             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16743             cells: cap[3].replace(/\n$/, '').split('\n')
16744           };
16745     
16746           for (i = 0; i < item.align.length; i++) {
16747             if (/^ *-+: *$/.test(item.align[i])) {
16748               item.align[i] = 'right';
16749             } else if (/^ *:-+: *$/.test(item.align[i])) {
16750               item.align[i] = 'center';
16751             } else if (/^ *:-+ *$/.test(item.align[i])) {
16752               item.align[i] = 'left';
16753             } else {
16754               item.align[i] = null;
16755             }
16756           }
16757     
16758           for (i = 0; i < item.cells.length; i++) {
16759             item.cells[i] = item.cells[i].split(/ *\| */);
16760           }
16761     
16762           this.tokens.push(item);
16763     
16764           continue;
16765         }
16766     
16767         // lheading
16768         if (cap = this.rules.lheading.exec(src)) {
16769           src = src.substring(cap[0].length);
16770           this.tokens.push({
16771             type: 'heading',
16772             depth: cap[2] === '=' ? 1 : 2,
16773             text: cap[1]
16774           });
16775           continue;
16776         }
16777     
16778         // hr
16779         if (cap = this.rules.hr.exec(src)) {
16780           src = src.substring(cap[0].length);
16781           this.tokens.push({
16782             type: 'hr'
16783           });
16784           continue;
16785         }
16786     
16787         // blockquote
16788         if (cap = this.rules.blockquote.exec(src)) {
16789           src = src.substring(cap[0].length);
16790     
16791           this.tokens.push({
16792             type: 'blockquote_start'
16793           });
16794     
16795           cap = cap[0].replace(/^ *> ?/gm, '');
16796     
16797           // Pass `top` to keep the current
16798           // "toplevel" state. This is exactly
16799           // how markdown.pl works.
16800           this.token(cap, top, true);
16801     
16802           this.tokens.push({
16803             type: 'blockquote_end'
16804           });
16805     
16806           continue;
16807         }
16808     
16809         // list
16810         if (cap = this.rules.list.exec(src)) {
16811           src = src.substring(cap[0].length);
16812           bull = cap[2];
16813     
16814           this.tokens.push({
16815             type: 'list_start',
16816             ordered: bull.length > 1
16817           });
16818     
16819           // Get each top-level item.
16820           cap = cap[0].match(this.rules.item);
16821     
16822           next = false;
16823           l = cap.length;
16824           i = 0;
16825     
16826           for (; i < l; i++) {
16827             item = cap[i];
16828     
16829             // Remove the list item's bullet
16830             // so it is seen as the next token.
16831             space = item.length;
16832             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16833     
16834             // Outdent whatever the
16835             // list item contains. Hacky.
16836             if (~item.indexOf('\n ')) {
16837               space -= item.length;
16838               item = !this.options.pedantic
16839                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16840                 : item.replace(/^ {1,4}/gm, '');
16841             }
16842     
16843             // Determine whether the next list item belongs here.
16844             // Backpedal if it does not belong in this list.
16845             if (this.options.smartLists && i !== l - 1) {
16846               b = block.bullet.exec(cap[i + 1])[0];
16847               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16848                 src = cap.slice(i + 1).join('\n') + src;
16849                 i = l - 1;
16850               }
16851             }
16852     
16853             // Determine whether item is loose or not.
16854             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16855             // for discount behavior.
16856             loose = next || /\n\n(?!\s*$)/.test(item);
16857             if (i !== l - 1) {
16858               next = item.charAt(item.length - 1) === '\n';
16859               if (!loose) { loose = next; }
16860             }
16861     
16862             this.tokens.push({
16863               type: loose
16864                 ? 'loose_item_start'
16865                 : 'list_item_start'
16866             });
16867     
16868             // Recurse.
16869             this.token(item, false, bq);
16870     
16871             this.tokens.push({
16872               type: 'list_item_end'
16873             });
16874           }
16875     
16876           this.tokens.push({
16877             type: 'list_end'
16878           });
16879     
16880           continue;
16881         }
16882     
16883         // html
16884         if (cap = this.rules.html.exec(src)) {
16885           src = src.substring(cap[0].length);
16886           this.tokens.push({
16887             type: this.options.sanitize
16888               ? 'paragraph'
16889               : 'html',
16890             pre: !this.options.sanitizer
16891               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
16892             text: cap[0]
16893           });
16894           continue;
16895         }
16896     
16897         // def
16898         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
16899           src = src.substring(cap[0].length);
16900           this.tokens.links[cap[1].toLowerCase()] = {
16901             href: cap[2],
16902             title: cap[3]
16903           };
16904           continue;
16905         }
16906     
16907         // table (gfm)
16908         if (top && (cap = this.rules.table.exec(src))) {
16909           src = src.substring(cap[0].length);
16910     
16911           item = {
16912             type: 'table',
16913             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16914             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16915             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
16916           };
16917     
16918           for (i = 0; i < item.align.length; i++) {
16919             if (/^ *-+: *$/.test(item.align[i])) {
16920               item.align[i] = 'right';
16921             } else if (/^ *:-+: *$/.test(item.align[i])) {
16922               item.align[i] = 'center';
16923             } else if (/^ *:-+ *$/.test(item.align[i])) {
16924               item.align[i] = 'left';
16925             } else {
16926               item.align[i] = null;
16927             }
16928           }
16929     
16930           for (i = 0; i < item.cells.length; i++) {
16931             item.cells[i] = item.cells[i]
16932               .replace(/^ *\| *| *\| *$/g, '')
16933               .split(/ *\| */);
16934           }
16935     
16936           this.tokens.push(item);
16937     
16938           continue;
16939         }
16940     
16941         // top-level paragraph
16942         if (top && (cap = this.rules.paragraph.exec(src))) {
16943           src = src.substring(cap[0].length);
16944           this.tokens.push({
16945             type: 'paragraph',
16946             text: cap[1].charAt(cap[1].length - 1) === '\n'
16947               ? cap[1].slice(0, -1)
16948               : cap[1]
16949           });
16950           continue;
16951         }
16952     
16953         // text
16954         if (cap = this.rules.text.exec(src)) {
16955           // Top-level should never reach here.
16956           src = src.substring(cap[0].length);
16957           this.tokens.push({
16958             type: 'text',
16959             text: cap[0]
16960           });
16961           continue;
16962         }
16963     
16964         if (src) {
16965           throw new
16966             Error('Infinite loop on byte: ' + src.charCodeAt(0));
16967         }
16968       }
16969     
16970       return this.tokens;
16971     };
16972     
16973     /**
16974      * Inline-Level Grammar
16975      */
16976     
16977     var inline = {
16978       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
16979       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
16980       url: noop,
16981       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
16982       link: /^!?\[(inside)\]\(href\)/,
16983       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
16984       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
16985       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
16986       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
16987       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
16988       br: /^ {2,}\n(?!\s*$)/,
16989       del: noop,
16990       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
16991     };
16992     
16993     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
16994     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
16995     
16996     inline.link = replace(inline.link)
16997       ('inside', inline._inside)
16998       ('href', inline._href)
16999       ();
17000     
17001     inline.reflink = replace(inline.reflink)
17002       ('inside', inline._inside)
17003       ();
17004     
17005     /**
17006      * Normal Inline Grammar
17007      */
17008     
17009     inline.normal = merge({}, inline);
17010     
17011     /**
17012      * Pedantic Inline Grammar
17013      */
17014     
17015     inline.pedantic = merge({}, inline.normal, {
17016       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17017       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17018     });
17019     
17020     /**
17021      * GFM Inline Grammar
17022      */
17023     
17024     inline.gfm = merge({}, inline.normal, {
17025       escape: replace(inline.escape)('])', '~|])')(),
17026       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17027       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17028       text: replace(inline.text)
17029         (']|', '~]|')
17030         ('|', '|https?://|')
17031         ()
17032     });
17033     
17034     /**
17035      * GFM + Line Breaks Inline Grammar
17036      */
17037     
17038     inline.breaks = merge({}, inline.gfm, {
17039       br: replace(inline.br)('{2,}', '*')(),
17040       text: replace(inline.gfm.text)('{2,}', '*')()
17041     });
17042     
17043     /**
17044      * Inline Lexer & Compiler
17045      */
17046     
17047     function InlineLexer(links, options) {
17048       this.options = options || marked.defaults;
17049       this.links = links;
17050       this.rules = inline.normal;
17051       this.renderer = this.options.renderer || new Renderer;
17052       this.renderer.options = this.options;
17053     
17054       if (!this.links) {
17055         throw new
17056           Error('Tokens array requires a `links` property.');
17057       }
17058     
17059       if (this.options.gfm) {
17060         if (this.options.breaks) {
17061           this.rules = inline.breaks;
17062         } else {
17063           this.rules = inline.gfm;
17064         }
17065       } else if (this.options.pedantic) {
17066         this.rules = inline.pedantic;
17067       }
17068     }
17069     
17070     /**
17071      * Expose Inline Rules
17072      */
17073     
17074     InlineLexer.rules = inline;
17075     
17076     /**
17077      * Static Lexing/Compiling Method
17078      */
17079     
17080     InlineLexer.output = function(src, links, options) {
17081       var inline = new InlineLexer(links, options);
17082       return inline.output(src);
17083     };
17084     
17085     /**
17086      * Lexing/Compiling
17087      */
17088     
17089     InlineLexer.prototype.output = function(src) {
17090       var out = ''
17091         , link
17092         , text
17093         , href
17094         , cap;
17095     
17096       while (src) {
17097         // escape
17098         if (cap = this.rules.escape.exec(src)) {
17099           src = src.substring(cap[0].length);
17100           out += cap[1];
17101           continue;
17102         }
17103     
17104         // autolink
17105         if (cap = this.rules.autolink.exec(src)) {
17106           src = src.substring(cap[0].length);
17107           if (cap[2] === '@') {
17108             text = cap[1].charAt(6) === ':'
17109               ? this.mangle(cap[1].substring(7))
17110               : this.mangle(cap[1]);
17111             href = this.mangle('mailto:') + text;
17112           } else {
17113             text = escape(cap[1]);
17114             href = text;
17115           }
17116           out += this.renderer.link(href, null, text);
17117           continue;
17118         }
17119     
17120         // url (gfm)
17121         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17122           src = src.substring(cap[0].length);
17123           text = escape(cap[1]);
17124           href = text;
17125           out += this.renderer.link(href, null, text);
17126           continue;
17127         }
17128     
17129         // tag
17130         if (cap = this.rules.tag.exec(src)) {
17131           if (!this.inLink && /^<a /i.test(cap[0])) {
17132             this.inLink = true;
17133           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17134             this.inLink = false;
17135           }
17136           src = src.substring(cap[0].length);
17137           out += this.options.sanitize
17138             ? this.options.sanitizer
17139               ? this.options.sanitizer(cap[0])
17140               : escape(cap[0])
17141             : cap[0];
17142           continue;
17143         }
17144     
17145         // link
17146         if (cap = this.rules.link.exec(src)) {
17147           src = src.substring(cap[0].length);
17148           this.inLink = true;
17149           out += this.outputLink(cap, {
17150             href: cap[2],
17151             title: cap[3]
17152           });
17153           this.inLink = false;
17154           continue;
17155         }
17156     
17157         // reflink, nolink
17158         if ((cap = this.rules.reflink.exec(src))
17159             || (cap = this.rules.nolink.exec(src))) {
17160           src = src.substring(cap[0].length);
17161           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17162           link = this.links[link.toLowerCase()];
17163           if (!link || !link.href) {
17164             out += cap[0].charAt(0);
17165             src = cap[0].substring(1) + src;
17166             continue;
17167           }
17168           this.inLink = true;
17169           out += this.outputLink(cap, link);
17170           this.inLink = false;
17171           continue;
17172         }
17173     
17174         // strong
17175         if (cap = this.rules.strong.exec(src)) {
17176           src = src.substring(cap[0].length);
17177           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17178           continue;
17179         }
17180     
17181         // em
17182         if (cap = this.rules.em.exec(src)) {
17183           src = src.substring(cap[0].length);
17184           out += this.renderer.em(this.output(cap[2] || cap[1]));
17185           continue;
17186         }
17187     
17188         // code
17189         if (cap = this.rules.code.exec(src)) {
17190           src = src.substring(cap[0].length);
17191           out += this.renderer.codespan(escape(cap[2], true));
17192           continue;
17193         }
17194     
17195         // br
17196         if (cap = this.rules.br.exec(src)) {
17197           src = src.substring(cap[0].length);
17198           out += this.renderer.br();
17199           continue;
17200         }
17201     
17202         // del (gfm)
17203         if (cap = this.rules.del.exec(src)) {
17204           src = src.substring(cap[0].length);
17205           out += this.renderer.del(this.output(cap[1]));
17206           continue;
17207         }
17208     
17209         // text
17210         if (cap = this.rules.text.exec(src)) {
17211           src = src.substring(cap[0].length);
17212           out += this.renderer.text(escape(this.smartypants(cap[0])));
17213           continue;
17214         }
17215     
17216         if (src) {
17217           throw new
17218             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17219         }
17220       }
17221     
17222       return out;
17223     };
17224     
17225     /**
17226      * Compile Link
17227      */
17228     
17229     InlineLexer.prototype.outputLink = function(cap, link) {
17230       var href = escape(link.href)
17231         , title = link.title ? escape(link.title) : null;
17232     
17233       return cap[0].charAt(0) !== '!'
17234         ? this.renderer.link(href, title, this.output(cap[1]))
17235         : this.renderer.image(href, title, escape(cap[1]));
17236     };
17237     
17238     /**
17239      * Smartypants Transformations
17240      */
17241     
17242     InlineLexer.prototype.smartypants = function(text) {
17243       if (!this.options.smartypants)  { return text; }
17244       return text
17245         // em-dashes
17246         .replace(/---/g, '\u2014')
17247         // en-dashes
17248         .replace(/--/g, '\u2013')
17249         // opening singles
17250         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17251         // closing singles & apostrophes
17252         .replace(/'/g, '\u2019')
17253         // opening doubles
17254         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17255         // closing doubles
17256         .replace(/"/g, '\u201d')
17257         // ellipses
17258         .replace(/\.{3}/g, '\u2026');
17259     };
17260     
17261     /**
17262      * Mangle Links
17263      */
17264     
17265     InlineLexer.prototype.mangle = function(text) {
17266       if (!this.options.mangle) { return text; }
17267       var out = ''
17268         , l = text.length
17269         , i = 0
17270         , ch;
17271     
17272       for (; i < l; i++) {
17273         ch = text.charCodeAt(i);
17274         if (Math.random() > 0.5) {
17275           ch = 'x' + ch.toString(16);
17276         }
17277         out += '&#' + ch + ';';
17278       }
17279     
17280       return out;
17281     };
17282     
17283     /**
17284      * Renderer
17285      */
17286     
17287     function Renderer(options) {
17288       this.options = options || {};
17289     }
17290     
17291     Renderer.prototype.code = function(code, lang, escaped) {
17292       if (this.options.highlight) {
17293         var out = this.options.highlight(code, lang);
17294         if (out != null && out !== code) {
17295           escaped = true;
17296           code = out;
17297         }
17298       } else {
17299             // hack!!! - it's already escapeD?
17300             escaped = true;
17301       }
17302     
17303       if (!lang) {
17304         return '<pre><code>'
17305           + (escaped ? code : escape(code, true))
17306           + '\n</code></pre>';
17307       }
17308     
17309       return '<pre><code class="'
17310         + this.options.langPrefix
17311         + escape(lang, true)
17312         + '">'
17313         + (escaped ? code : escape(code, true))
17314         + '\n</code></pre>\n';
17315     };
17316     
17317     Renderer.prototype.blockquote = function(quote) {
17318       return '<blockquote>\n' + quote + '</blockquote>\n';
17319     };
17320     
17321     Renderer.prototype.html = function(html) {
17322       return html;
17323     };
17324     
17325     Renderer.prototype.heading = function(text, level, raw) {
17326       return '<h'
17327         + level
17328         + ' id="'
17329         + this.options.headerPrefix
17330         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17331         + '">'
17332         + text
17333         + '</h'
17334         + level
17335         + '>\n';
17336     };
17337     
17338     Renderer.prototype.hr = function() {
17339       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17340     };
17341     
17342     Renderer.prototype.list = function(body, ordered) {
17343       var type = ordered ? 'ol' : 'ul';
17344       return '<' + type + '>\n' + body + '</' + type + '>\n';
17345     };
17346     
17347     Renderer.prototype.listitem = function(text) {
17348       return '<li>' + text + '</li>\n';
17349     };
17350     
17351     Renderer.prototype.paragraph = function(text) {
17352       return '<p>' + text + '</p>\n';
17353     };
17354     
17355     Renderer.prototype.table = function(header, body) {
17356       return '<table class="table table-striped">\n'
17357         + '<thead>\n'
17358         + header
17359         + '</thead>\n'
17360         + '<tbody>\n'
17361         + body
17362         + '</tbody>\n'
17363         + '</table>\n';
17364     };
17365     
17366     Renderer.prototype.tablerow = function(content) {
17367       return '<tr>\n' + content + '</tr>\n';
17368     };
17369     
17370     Renderer.prototype.tablecell = function(content, flags) {
17371       var type = flags.header ? 'th' : 'td';
17372       var tag = flags.align
17373         ? '<' + type + ' style="text-align:' + flags.align + '">'
17374         : '<' + type + '>';
17375       return tag + content + '</' + type + '>\n';
17376     };
17377     
17378     // span level renderer
17379     Renderer.prototype.strong = function(text) {
17380       return '<strong>' + text + '</strong>';
17381     };
17382     
17383     Renderer.prototype.em = function(text) {
17384       return '<em>' + text + '</em>';
17385     };
17386     
17387     Renderer.prototype.codespan = function(text) {
17388       return '<code>' + text + '</code>';
17389     };
17390     
17391     Renderer.prototype.br = function() {
17392       return this.options.xhtml ? '<br/>' : '<br>';
17393     };
17394     
17395     Renderer.prototype.del = function(text) {
17396       return '<del>' + text + '</del>';
17397     };
17398     
17399     Renderer.prototype.link = function(href, title, text) {
17400       if (this.options.sanitize) {
17401         try {
17402           var prot = decodeURIComponent(unescape(href))
17403             .replace(/[^\w:]/g, '')
17404             .toLowerCase();
17405         } catch (e) {
17406           return '';
17407         }
17408         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17409           return '';
17410         }
17411       }
17412       var out = '<a href="' + href + '"';
17413       if (title) {
17414         out += ' title="' + title + '"';
17415       }
17416       out += '>' + text + '</a>';
17417       return out;
17418     };
17419     
17420     Renderer.prototype.image = function(href, title, text) {
17421       var out = '<img src="' + href + '" alt="' + text + '"';
17422       if (title) {
17423         out += ' title="' + title + '"';
17424       }
17425       out += this.options.xhtml ? '/>' : '>';
17426       return out;
17427     };
17428     
17429     Renderer.prototype.text = function(text) {
17430       return text;
17431     };
17432     
17433     /**
17434      * Parsing & Compiling
17435      */
17436     
17437     function Parser(options) {
17438       this.tokens = [];
17439       this.token = null;
17440       this.options = options || marked.defaults;
17441       this.options.renderer = this.options.renderer || new Renderer;
17442       this.renderer = this.options.renderer;
17443       this.renderer.options = this.options;
17444     }
17445     
17446     /**
17447      * Static Parse Method
17448      */
17449     
17450     Parser.parse = function(src, options, renderer) {
17451       var parser = new Parser(options, renderer);
17452       return parser.parse(src);
17453     };
17454     
17455     /**
17456      * Parse Loop
17457      */
17458     
17459     Parser.prototype.parse = function(src) {
17460       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17461       this.tokens = src.reverse();
17462     
17463       var out = '';
17464       while (this.next()) {
17465         out += this.tok();
17466       }
17467     
17468       return out;
17469     };
17470     
17471     /**
17472      * Next Token
17473      */
17474     
17475     Parser.prototype.next = function() {
17476       return this.token = this.tokens.pop();
17477     };
17478     
17479     /**
17480      * Preview Next Token
17481      */
17482     
17483     Parser.prototype.peek = function() {
17484       return this.tokens[this.tokens.length - 1] || 0;
17485     };
17486     
17487     /**
17488      * Parse Text Tokens
17489      */
17490     
17491     Parser.prototype.parseText = function() {
17492       var body = this.token.text;
17493     
17494       while (this.peek().type === 'text') {
17495         body += '\n' + this.next().text;
17496       }
17497     
17498       return this.inline.output(body);
17499     };
17500     
17501     /**
17502      * Parse Current Token
17503      */
17504     
17505     Parser.prototype.tok = function() {
17506       switch (this.token.type) {
17507         case 'space': {
17508           return '';
17509         }
17510         case 'hr': {
17511           return this.renderer.hr();
17512         }
17513         case 'heading': {
17514           return this.renderer.heading(
17515             this.inline.output(this.token.text),
17516             this.token.depth,
17517             this.token.text);
17518         }
17519         case 'code': {
17520           return this.renderer.code(this.token.text,
17521             this.token.lang,
17522             this.token.escaped);
17523         }
17524         case 'table': {
17525           var header = ''
17526             , body = ''
17527             , i
17528             , row
17529             , cell
17530             , flags
17531             , j;
17532     
17533           // header
17534           cell = '';
17535           for (i = 0; i < this.token.header.length; i++) {
17536             flags = { header: true, align: this.token.align[i] };
17537             cell += this.renderer.tablecell(
17538               this.inline.output(this.token.header[i]),
17539               { header: true, align: this.token.align[i] }
17540             );
17541           }
17542           header += this.renderer.tablerow(cell);
17543     
17544           for (i = 0; i < this.token.cells.length; i++) {
17545             row = this.token.cells[i];
17546     
17547             cell = '';
17548             for (j = 0; j < row.length; j++) {
17549               cell += this.renderer.tablecell(
17550                 this.inline.output(row[j]),
17551                 { header: false, align: this.token.align[j] }
17552               );
17553             }
17554     
17555             body += this.renderer.tablerow(cell);
17556           }
17557           return this.renderer.table(header, body);
17558         }
17559         case 'blockquote_start': {
17560           var body = '';
17561     
17562           while (this.next().type !== 'blockquote_end') {
17563             body += this.tok();
17564           }
17565     
17566           return this.renderer.blockquote(body);
17567         }
17568         case 'list_start': {
17569           var body = ''
17570             , ordered = this.token.ordered;
17571     
17572           while (this.next().type !== 'list_end') {
17573             body += this.tok();
17574           }
17575     
17576           return this.renderer.list(body, ordered);
17577         }
17578         case 'list_item_start': {
17579           var body = '';
17580     
17581           while (this.next().type !== 'list_item_end') {
17582             body += this.token.type === 'text'
17583               ? this.parseText()
17584               : this.tok();
17585           }
17586     
17587           return this.renderer.listitem(body);
17588         }
17589         case 'loose_item_start': {
17590           var body = '';
17591     
17592           while (this.next().type !== 'list_item_end') {
17593             body += this.tok();
17594           }
17595     
17596           return this.renderer.listitem(body);
17597         }
17598         case 'html': {
17599           var html = !this.token.pre && !this.options.pedantic
17600             ? this.inline.output(this.token.text)
17601             : this.token.text;
17602           return this.renderer.html(html);
17603         }
17604         case 'paragraph': {
17605           return this.renderer.paragraph(this.inline.output(this.token.text));
17606         }
17607         case 'text': {
17608           return this.renderer.paragraph(this.parseText());
17609         }
17610       }
17611     };
17612     
17613     /**
17614      * Helpers
17615      */
17616     
17617     function escape(html, encode) {
17618       return html
17619         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17620         .replace(/</g, '&lt;')
17621         .replace(/>/g, '&gt;')
17622         .replace(/"/g, '&quot;')
17623         .replace(/'/g, '&#39;');
17624     }
17625     
17626     function unescape(html) {
17627         // explicitly match decimal, hex, and named HTML entities 
17628       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17629         n = n.toLowerCase();
17630         if (n === 'colon') { return ':'; }
17631         if (n.charAt(0) === '#') {
17632           return n.charAt(1) === 'x'
17633             ? String.fromCharCode(parseInt(n.substring(2), 16))
17634             : String.fromCharCode(+n.substring(1));
17635         }
17636         return '';
17637       });
17638     }
17639     
17640     function replace(regex, opt) {
17641       regex = regex.source;
17642       opt = opt || '';
17643       return function self(name, val) {
17644         if (!name) { return new RegExp(regex, opt); }
17645         val = val.source || val;
17646         val = val.replace(/(^|[^\[])\^/g, '$1');
17647         regex = regex.replace(name, val);
17648         return self;
17649       };
17650     }
17651     
17652     function noop() {}
17653     noop.exec = noop;
17654     
17655     function merge(obj) {
17656       var i = 1
17657         , target
17658         , key;
17659     
17660       for (; i < arguments.length; i++) {
17661         target = arguments[i];
17662         for (key in target) {
17663           if (Object.prototype.hasOwnProperty.call(target, key)) {
17664             obj[key] = target[key];
17665           }
17666         }
17667       }
17668     
17669       return obj;
17670     }
17671     
17672     
17673     /**
17674      * Marked
17675      */
17676     
17677     function marked(src, opt, callback) {
17678       if (callback || typeof opt === 'function') {
17679         if (!callback) {
17680           callback = opt;
17681           opt = null;
17682         }
17683     
17684         opt = merge({}, marked.defaults, opt || {});
17685     
17686         var highlight = opt.highlight
17687           , tokens
17688           , pending
17689           , i = 0;
17690     
17691         try {
17692           tokens = Lexer.lex(src, opt)
17693         } catch (e) {
17694           return callback(e);
17695         }
17696     
17697         pending = tokens.length;
17698     
17699         var done = function(err) {
17700           if (err) {
17701             opt.highlight = highlight;
17702             return callback(err);
17703           }
17704     
17705           var out;
17706     
17707           try {
17708             out = Parser.parse(tokens, opt);
17709           } catch (e) {
17710             err = e;
17711           }
17712     
17713           opt.highlight = highlight;
17714     
17715           return err
17716             ? callback(err)
17717             : callback(null, out);
17718         };
17719     
17720         if (!highlight || highlight.length < 3) {
17721           return done();
17722         }
17723     
17724         delete opt.highlight;
17725     
17726         if (!pending) { return done(); }
17727     
17728         for (; i < tokens.length; i++) {
17729           (function(token) {
17730             if (token.type !== 'code') {
17731               return --pending || done();
17732             }
17733             return highlight(token.text, token.lang, function(err, code) {
17734               if (err) { return done(err); }
17735               if (code == null || code === token.text) {
17736                 return --pending || done();
17737               }
17738               token.text = code;
17739               token.escaped = true;
17740               --pending || done();
17741             });
17742           })(tokens[i]);
17743         }
17744     
17745         return;
17746       }
17747       try {
17748         if (opt) { opt = merge({}, marked.defaults, opt); }
17749         return Parser.parse(Lexer.lex(src, opt), opt);
17750       } catch (e) {
17751         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17752         if ((opt || marked.defaults).silent) {
17753           return '<p>An error occured:</p><pre>'
17754             + escape(e.message + '', true)
17755             + '</pre>';
17756         }
17757         throw e;
17758       }
17759     }
17760     
17761     /**
17762      * Options
17763      */
17764     
17765     marked.options =
17766     marked.setOptions = function(opt) {
17767       merge(marked.defaults, opt);
17768       return marked;
17769     };
17770     
17771     marked.defaults = {
17772       gfm: true,
17773       tables: true,
17774       breaks: false,
17775       pedantic: false,
17776       sanitize: false,
17777       sanitizer: null,
17778       mangle: true,
17779       smartLists: false,
17780       silent: false,
17781       highlight: null,
17782       langPrefix: 'lang-',
17783       smartypants: false,
17784       headerPrefix: '',
17785       renderer: new Renderer,
17786       xhtml: false
17787     };
17788     
17789     /**
17790      * Expose
17791      */
17792     
17793     marked.Parser = Parser;
17794     marked.parser = Parser.parse;
17795     
17796     marked.Renderer = Renderer;
17797     
17798     marked.Lexer = Lexer;
17799     marked.lexer = Lexer.lex;
17800     
17801     marked.InlineLexer = InlineLexer;
17802     marked.inlineLexer = InlineLexer.output;
17803     
17804     marked.parse = marked;
17805     
17806     Roo.Markdown.marked = marked;
17807
17808 })();/*
17809  * Based on:
17810  * Ext JS Library 1.1.1
17811  * Copyright(c) 2006-2007, Ext JS, LLC.
17812  *
17813  * Originally Released Under LGPL - original licence link has changed is not relivant.
17814  *
17815  * Fork - LGPL
17816  * <script type="text/javascript">
17817  */
17818
17819
17820
17821 /*
17822  * These classes are derivatives of the similarly named classes in the YUI Library.
17823  * The original license:
17824  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17825  * Code licensed under the BSD License:
17826  * http://developer.yahoo.net/yui/license.txt
17827  */
17828
17829 (function() {
17830
17831 var Event=Roo.EventManager;
17832 var Dom=Roo.lib.Dom;
17833
17834 /**
17835  * @class Roo.dd.DragDrop
17836  * @extends Roo.util.Observable
17837  * Defines the interface and base operation of items that that can be
17838  * dragged or can be drop targets.  It was designed to be extended, overriding
17839  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17840  * Up to three html elements can be associated with a DragDrop instance:
17841  * <ul>
17842  * <li>linked element: the element that is passed into the constructor.
17843  * This is the element which defines the boundaries for interaction with
17844  * other DragDrop objects.</li>
17845  * <li>handle element(s): The drag operation only occurs if the element that
17846  * was clicked matches a handle element.  By default this is the linked
17847  * element, but there are times that you will want only a portion of the
17848  * linked element to initiate the drag operation, and the setHandleElId()
17849  * method provides a way to define this.</li>
17850  * <li>drag element: this represents the element that would be moved along
17851  * with the cursor during a drag operation.  By default, this is the linked
17852  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17853  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17854  * </li>
17855  * </ul>
17856  * This class should not be instantiated until the onload event to ensure that
17857  * the associated elements are available.
17858  * The following would define a DragDrop obj that would interact with any
17859  * other DragDrop obj in the "group1" group:
17860  * <pre>
17861  *  dd = new Roo.dd.DragDrop("div1", "group1");
17862  * </pre>
17863  * Since none of the event handlers have been implemented, nothing would
17864  * actually happen if you were to run the code above.  Normally you would
17865  * override this class or one of the default implementations, but you can
17866  * also override the methods you want on an instance of the class...
17867  * <pre>
17868  *  dd.onDragDrop = function(e, id) {
17869  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
17870  *  }
17871  * </pre>
17872  * @constructor
17873  * @param {String} id of the element that is linked to this instance
17874  * @param {String} sGroup the group of related DragDrop objects
17875  * @param {object} config an object containing configurable attributes
17876  *                Valid properties for DragDrop:
17877  *                    padding, isTarget, maintainOffset, primaryButtonOnly
17878  */
17879 Roo.dd.DragDrop = function(id, sGroup, config) {
17880     if (id) {
17881         this.init(id, sGroup, config);
17882     }
17883     
17884 };
17885
17886 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
17887
17888     /**
17889      * The id of the element associated with this object.  This is what we
17890      * refer to as the "linked element" because the size and position of
17891      * this element is used to determine when the drag and drop objects have
17892      * interacted.
17893      * @property id
17894      * @type String
17895      */
17896     id: null,
17897
17898     /**
17899      * Configuration attributes passed into the constructor
17900      * @property config
17901      * @type object
17902      */
17903     config: null,
17904
17905     /**
17906      * The id of the element that will be dragged.  By default this is same
17907      * as the linked element , but could be changed to another element. Ex:
17908      * Roo.dd.DDProxy
17909      * @property dragElId
17910      * @type String
17911      * @private
17912      */
17913     dragElId: null,
17914
17915     /**
17916      * the id of the element that initiates the drag operation.  By default
17917      * this is the linked element, but could be changed to be a child of this
17918      * element.  This lets us do things like only starting the drag when the
17919      * header element within the linked html element is clicked.
17920      * @property handleElId
17921      * @type String
17922      * @private
17923      */
17924     handleElId: null,
17925
17926     /**
17927      * An associative array of HTML tags that will be ignored if clicked.
17928      * @property invalidHandleTypes
17929      * @type {string: string}
17930      */
17931     invalidHandleTypes: null,
17932
17933     /**
17934      * An associative array of ids for elements that will be ignored if clicked
17935      * @property invalidHandleIds
17936      * @type {string: string}
17937      */
17938     invalidHandleIds: null,
17939
17940     /**
17941      * An indexted array of css class names for elements that will be ignored
17942      * if clicked.
17943      * @property invalidHandleClasses
17944      * @type string[]
17945      */
17946     invalidHandleClasses: null,
17947
17948     /**
17949      * The linked element's absolute X position at the time the drag was
17950      * started
17951      * @property startPageX
17952      * @type int
17953      * @private
17954      */
17955     startPageX: 0,
17956
17957     /**
17958      * The linked element's absolute X position at the time the drag was
17959      * started
17960      * @property startPageY
17961      * @type int
17962      * @private
17963      */
17964     startPageY: 0,
17965
17966     /**
17967      * The group defines a logical collection of DragDrop objects that are
17968      * related.  Instances only get events when interacting with other
17969      * DragDrop object in the same group.  This lets us define multiple
17970      * groups using a single DragDrop subclass if we want.
17971      * @property groups
17972      * @type {string: string}
17973      */
17974     groups: null,
17975
17976     /**
17977      * Individual drag/drop instances can be locked.  This will prevent
17978      * onmousedown start drag.
17979      * @property locked
17980      * @type boolean
17981      * @private
17982      */
17983     locked: false,
17984
17985     /**
17986      * Lock this instance
17987      * @method lock
17988      */
17989     lock: function() { this.locked = true; },
17990
17991     /**
17992      * Unlock this instace
17993      * @method unlock
17994      */
17995     unlock: function() { this.locked = false; },
17996
17997     /**
17998      * By default, all insances can be a drop target.  This can be disabled by
17999      * setting isTarget to false.
18000      * @method isTarget
18001      * @type boolean
18002      */
18003     isTarget: true,
18004
18005     /**
18006      * The padding configured for this drag and drop object for calculating
18007      * the drop zone intersection with this object.
18008      * @method padding
18009      * @type int[]
18010      */
18011     padding: null,
18012
18013     /**
18014      * Cached reference to the linked element
18015      * @property _domRef
18016      * @private
18017      */
18018     _domRef: null,
18019
18020     /**
18021      * Internal typeof flag
18022      * @property __ygDragDrop
18023      * @private
18024      */
18025     __ygDragDrop: true,
18026
18027     /**
18028      * Set to true when horizontal contraints are applied
18029      * @property constrainX
18030      * @type boolean
18031      * @private
18032      */
18033     constrainX: false,
18034
18035     /**
18036      * Set to true when vertical contraints are applied
18037      * @property constrainY
18038      * @type boolean
18039      * @private
18040      */
18041     constrainY: false,
18042
18043     /**
18044      * The left constraint
18045      * @property minX
18046      * @type int
18047      * @private
18048      */
18049     minX: 0,
18050
18051     /**
18052      * The right constraint
18053      * @property maxX
18054      * @type int
18055      * @private
18056      */
18057     maxX: 0,
18058
18059     /**
18060      * The up constraint
18061      * @property minY
18062      * @type int
18063      * @type int
18064      * @private
18065      */
18066     minY: 0,
18067
18068     /**
18069      * The down constraint
18070      * @property maxY
18071      * @type int
18072      * @private
18073      */
18074     maxY: 0,
18075
18076     /**
18077      * Maintain offsets when we resetconstraints.  Set to true when you want
18078      * the position of the element relative to its parent to stay the same
18079      * when the page changes
18080      *
18081      * @property maintainOffset
18082      * @type boolean
18083      */
18084     maintainOffset: false,
18085
18086     /**
18087      * Array of pixel locations the element will snap to if we specified a
18088      * horizontal graduation/interval.  This array is generated automatically
18089      * when you define a tick interval.
18090      * @property xTicks
18091      * @type int[]
18092      */
18093     xTicks: null,
18094
18095     /**
18096      * Array of pixel locations the element will snap to if we specified a
18097      * vertical graduation/interval.  This array is generated automatically
18098      * when you define a tick interval.
18099      * @property yTicks
18100      * @type int[]
18101      */
18102     yTicks: null,
18103
18104     /**
18105      * By default the drag and drop instance will only respond to the primary
18106      * button click (left button for a right-handed mouse).  Set to true to
18107      * allow drag and drop to start with any mouse click that is propogated
18108      * by the browser
18109      * @property primaryButtonOnly
18110      * @type boolean
18111      */
18112     primaryButtonOnly: true,
18113
18114     /**
18115      * The availabe property is false until the linked dom element is accessible.
18116      * @property available
18117      * @type boolean
18118      */
18119     available: false,
18120
18121     /**
18122      * By default, drags can only be initiated if the mousedown occurs in the
18123      * region the linked element is.  This is done in part to work around a
18124      * bug in some browsers that mis-report the mousedown if the previous
18125      * mouseup happened outside of the window.  This property is set to true
18126      * if outer handles are defined.
18127      *
18128      * @property hasOuterHandles
18129      * @type boolean
18130      * @default false
18131      */
18132     hasOuterHandles: false,
18133
18134     /**
18135      * Code that executes immediately before the startDrag event
18136      * @method b4StartDrag
18137      * @private
18138      */
18139     b4StartDrag: function(x, y) { },
18140
18141     /**
18142      * Abstract method called after a drag/drop object is clicked
18143      * and the drag or mousedown time thresholds have beeen met.
18144      * @method startDrag
18145      * @param {int} X click location
18146      * @param {int} Y click location
18147      */
18148     startDrag: function(x, y) { /* override this */ },
18149
18150     /**
18151      * Code that executes immediately before the onDrag event
18152      * @method b4Drag
18153      * @private
18154      */
18155     b4Drag: function(e) { },
18156
18157     /**
18158      * Abstract method called during the onMouseMove event while dragging an
18159      * object.
18160      * @method onDrag
18161      * @param {Event} e the mousemove event
18162      */
18163     onDrag: function(e) { /* override this */ },
18164
18165     /**
18166      * Abstract method called when this element fist begins hovering over
18167      * another DragDrop obj
18168      * @method onDragEnter
18169      * @param {Event} e the mousemove event
18170      * @param {String|DragDrop[]} id In POINT mode, the element
18171      * id this is hovering over.  In INTERSECT mode, an array of one or more
18172      * dragdrop items being hovered over.
18173      */
18174     onDragEnter: function(e, id) { /* override this */ },
18175
18176     /**
18177      * Code that executes immediately before the onDragOver event
18178      * @method b4DragOver
18179      * @private
18180      */
18181     b4DragOver: function(e) { },
18182
18183     /**
18184      * Abstract method called when this element is hovering over another
18185      * DragDrop obj
18186      * @method onDragOver
18187      * @param {Event} e the mousemove event
18188      * @param {String|DragDrop[]} id In POINT mode, the element
18189      * id this is hovering over.  In INTERSECT mode, an array of dd items
18190      * being hovered over.
18191      */
18192     onDragOver: function(e, id) { /* override this */ },
18193
18194     /**
18195      * Code that executes immediately before the onDragOut event
18196      * @method b4DragOut
18197      * @private
18198      */
18199     b4DragOut: function(e) { },
18200
18201     /**
18202      * Abstract method called when we are no longer hovering over an element
18203      * @method onDragOut
18204      * @param {Event} e the mousemove event
18205      * @param {String|DragDrop[]} id In POINT mode, the element
18206      * id this was hovering over.  In INTERSECT mode, an array of dd items
18207      * that the mouse is no longer over.
18208      */
18209     onDragOut: function(e, id) { /* override this */ },
18210
18211     /**
18212      * Code that executes immediately before the onDragDrop event
18213      * @method b4DragDrop
18214      * @private
18215      */
18216     b4DragDrop: function(e) { },
18217
18218     /**
18219      * Abstract method called when this item is dropped on another DragDrop
18220      * obj
18221      * @method onDragDrop
18222      * @param {Event} e the mouseup event
18223      * @param {String|DragDrop[]} id In POINT mode, the element
18224      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18225      * was dropped on.
18226      */
18227     onDragDrop: function(e, id) { /* override this */ },
18228
18229     /**
18230      * Abstract method called when this item is dropped on an area with no
18231      * drop target
18232      * @method onInvalidDrop
18233      * @param {Event} e the mouseup event
18234      */
18235     onInvalidDrop: function(e) { /* override this */ },
18236
18237     /**
18238      * Code that executes immediately before the endDrag event
18239      * @method b4EndDrag
18240      * @private
18241      */
18242     b4EndDrag: function(e) { },
18243
18244     /**
18245      * Fired when we are done dragging the object
18246      * @method endDrag
18247      * @param {Event} e the mouseup event
18248      */
18249     endDrag: function(e) { /* override this */ },
18250
18251     /**
18252      * Code executed immediately before the onMouseDown event
18253      * @method b4MouseDown
18254      * @param {Event} e the mousedown event
18255      * @private
18256      */
18257     b4MouseDown: function(e) {  },
18258
18259     /**
18260      * Event handler that fires when a drag/drop obj gets a mousedown
18261      * @method onMouseDown
18262      * @param {Event} e the mousedown event
18263      */
18264     onMouseDown: function(e) { /* override this */ },
18265
18266     /**
18267      * Event handler that fires when a drag/drop obj gets a mouseup
18268      * @method onMouseUp
18269      * @param {Event} e the mouseup event
18270      */
18271     onMouseUp: function(e) { /* override this */ },
18272
18273     /**
18274      * Override the onAvailable method to do what is needed after the initial
18275      * position was determined.
18276      * @method onAvailable
18277      */
18278     onAvailable: function () {
18279     },
18280
18281     /*
18282      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18283      * @type Object
18284      */
18285     defaultPadding : {left:0, right:0, top:0, bottom:0},
18286
18287     /*
18288      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18289  *
18290  * Usage:
18291  <pre><code>
18292  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18293                 { dragElId: "existingProxyDiv" });
18294  dd.startDrag = function(){
18295      this.constrainTo("parent-id");
18296  };
18297  </code></pre>
18298  * Or you can initalize it using the {@link Roo.Element} object:
18299  <pre><code>
18300  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18301      startDrag : function(){
18302          this.constrainTo("parent-id");
18303      }
18304  });
18305  </code></pre>
18306      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18307      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18308      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18309      * an object containing the sides to pad. For example: {right:10, bottom:10}
18310      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18311      */
18312     constrainTo : function(constrainTo, pad, inContent){
18313         if(typeof pad == "number"){
18314             pad = {left: pad, right:pad, top:pad, bottom:pad};
18315         }
18316         pad = pad || this.defaultPadding;
18317         var b = Roo.get(this.getEl()).getBox();
18318         var ce = Roo.get(constrainTo);
18319         var s = ce.getScroll();
18320         var c, cd = ce.dom;
18321         if(cd == document.body){
18322             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18323         }else{
18324             xy = ce.getXY();
18325             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18326         }
18327
18328
18329         var topSpace = b.y - c.y;
18330         var leftSpace = b.x - c.x;
18331
18332         this.resetConstraints();
18333         this.setXConstraint(leftSpace - (pad.left||0), // left
18334                 c.width - leftSpace - b.width - (pad.right||0) //right
18335         );
18336         this.setYConstraint(topSpace - (pad.top||0), //top
18337                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18338         );
18339     },
18340
18341     /**
18342      * Returns a reference to the linked element
18343      * @method getEl
18344      * @return {HTMLElement} the html element
18345      */
18346     getEl: function() {
18347         if (!this._domRef) {
18348             this._domRef = Roo.getDom(this.id);
18349         }
18350
18351         return this._domRef;
18352     },
18353
18354     /**
18355      * Returns a reference to the actual element to drag.  By default this is
18356      * the same as the html element, but it can be assigned to another
18357      * element. An example of this can be found in Roo.dd.DDProxy
18358      * @method getDragEl
18359      * @return {HTMLElement} the html element
18360      */
18361     getDragEl: function() {
18362         return Roo.getDom(this.dragElId);
18363     },
18364
18365     /**
18366      * Sets up the DragDrop object.  Must be called in the constructor of any
18367      * Roo.dd.DragDrop subclass
18368      * @method init
18369      * @param id the id of the linked element
18370      * @param {String} sGroup the group of related items
18371      * @param {object} config configuration attributes
18372      */
18373     init: function(id, sGroup, config) {
18374         this.initTarget(id, sGroup, config);
18375         if (!Roo.isTouch) {
18376             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18377         }
18378         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18379         // Event.on(this.id, "selectstart", Event.preventDefault);
18380     },
18381
18382     /**
18383      * Initializes Targeting functionality only... the object does not
18384      * get a mousedown handler.
18385      * @method initTarget
18386      * @param id the id of the linked element
18387      * @param {String} sGroup the group of related items
18388      * @param {object} config configuration attributes
18389      */
18390     initTarget: function(id, sGroup, config) {
18391
18392         // configuration attributes
18393         this.config = config || {};
18394
18395         // create a local reference to the drag and drop manager
18396         this.DDM = Roo.dd.DDM;
18397         // initialize the groups array
18398         this.groups = {};
18399
18400         // assume that we have an element reference instead of an id if the
18401         // parameter is not a string
18402         if (typeof id !== "string") {
18403             id = Roo.id(id);
18404         }
18405
18406         // set the id
18407         this.id = id;
18408
18409         // add to an interaction group
18410         this.addToGroup((sGroup) ? sGroup : "default");
18411
18412         // We don't want to register this as the handle with the manager
18413         // so we just set the id rather than calling the setter.
18414         this.handleElId = id;
18415
18416         // the linked element is the element that gets dragged by default
18417         this.setDragElId(id);
18418
18419         // by default, clicked anchors will not start drag operations.
18420         this.invalidHandleTypes = { A: "A" };
18421         this.invalidHandleIds = {};
18422         this.invalidHandleClasses = [];
18423
18424         this.applyConfig();
18425
18426         this.handleOnAvailable();
18427     },
18428
18429     /**
18430      * Applies the configuration parameters that were passed into the constructor.
18431      * This is supposed to happen at each level through the inheritance chain.  So
18432      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18433      * DragDrop in order to get all of the parameters that are available in
18434      * each object.
18435      * @method applyConfig
18436      */
18437     applyConfig: function() {
18438
18439         // configurable properties:
18440         //    padding, isTarget, maintainOffset, primaryButtonOnly
18441         this.padding           = this.config.padding || [0, 0, 0, 0];
18442         this.isTarget          = (this.config.isTarget !== false);
18443         this.maintainOffset    = (this.config.maintainOffset);
18444         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18445
18446     },
18447
18448     /**
18449      * Executed when the linked element is available
18450      * @method handleOnAvailable
18451      * @private
18452      */
18453     handleOnAvailable: function() {
18454         this.available = true;
18455         this.resetConstraints();
18456         this.onAvailable();
18457     },
18458
18459      /**
18460      * Configures the padding for the target zone in px.  Effectively expands
18461      * (or reduces) the virtual object size for targeting calculations.
18462      * Supports css-style shorthand; if only one parameter is passed, all sides
18463      * will have that padding, and if only two are passed, the top and bottom
18464      * will have the first param, the left and right the second.
18465      * @method setPadding
18466      * @param {int} iTop    Top pad
18467      * @param {int} iRight  Right pad
18468      * @param {int} iBot    Bot pad
18469      * @param {int} iLeft   Left pad
18470      */
18471     setPadding: function(iTop, iRight, iBot, iLeft) {
18472         // this.padding = [iLeft, iRight, iTop, iBot];
18473         if (!iRight && 0 !== iRight) {
18474             this.padding = [iTop, iTop, iTop, iTop];
18475         } else if (!iBot && 0 !== iBot) {
18476             this.padding = [iTop, iRight, iTop, iRight];
18477         } else {
18478             this.padding = [iTop, iRight, iBot, iLeft];
18479         }
18480     },
18481
18482     /**
18483      * Stores the initial placement of the linked element.
18484      * @method setInitialPosition
18485      * @param {int} diffX   the X offset, default 0
18486      * @param {int} diffY   the Y offset, default 0
18487      */
18488     setInitPosition: function(diffX, diffY) {
18489         var el = this.getEl();
18490
18491         if (!this.DDM.verifyEl(el)) {
18492             return;
18493         }
18494
18495         var dx = diffX || 0;
18496         var dy = diffY || 0;
18497
18498         var p = Dom.getXY( el );
18499
18500         this.initPageX = p[0] - dx;
18501         this.initPageY = p[1] - dy;
18502
18503         this.lastPageX = p[0];
18504         this.lastPageY = p[1];
18505
18506
18507         this.setStartPosition(p);
18508     },
18509
18510     /**
18511      * Sets the start position of the element.  This is set when the obj
18512      * is initialized, the reset when a drag is started.
18513      * @method setStartPosition
18514      * @param pos current position (from previous lookup)
18515      * @private
18516      */
18517     setStartPosition: function(pos) {
18518         var p = pos || Dom.getXY( this.getEl() );
18519         this.deltaSetXY = null;
18520
18521         this.startPageX = p[0];
18522         this.startPageY = p[1];
18523     },
18524
18525     /**
18526      * Add this instance to a group of related drag/drop objects.  All
18527      * instances belong to at least one group, and can belong to as many
18528      * groups as needed.
18529      * @method addToGroup
18530      * @param sGroup {string} the name of the group
18531      */
18532     addToGroup: function(sGroup) {
18533         this.groups[sGroup] = true;
18534         this.DDM.regDragDrop(this, sGroup);
18535     },
18536
18537     /**
18538      * Remove's this instance from the supplied interaction group
18539      * @method removeFromGroup
18540      * @param {string}  sGroup  The group to drop
18541      */
18542     removeFromGroup: function(sGroup) {
18543         if (this.groups[sGroup]) {
18544             delete this.groups[sGroup];
18545         }
18546
18547         this.DDM.removeDDFromGroup(this, sGroup);
18548     },
18549
18550     /**
18551      * Allows you to specify that an element other than the linked element
18552      * will be moved with the cursor during a drag
18553      * @method setDragElId
18554      * @param id {string} the id of the element that will be used to initiate the drag
18555      */
18556     setDragElId: function(id) {
18557         this.dragElId = id;
18558     },
18559
18560     /**
18561      * Allows you to specify a child of the linked element that should be
18562      * used to initiate the drag operation.  An example of this would be if
18563      * you have a content div with text and links.  Clicking anywhere in the
18564      * content area would normally start the drag operation.  Use this method
18565      * to specify that an element inside of the content div is the element
18566      * that starts the drag operation.
18567      * @method setHandleElId
18568      * @param id {string} the id of the element that will be used to
18569      * initiate the drag.
18570      */
18571     setHandleElId: function(id) {
18572         if (typeof id !== "string") {
18573             id = Roo.id(id);
18574         }
18575         this.handleElId = id;
18576         this.DDM.regHandle(this.id, id);
18577     },
18578
18579     /**
18580      * Allows you to set an element outside of the linked element as a drag
18581      * handle
18582      * @method setOuterHandleElId
18583      * @param id the id of the element that will be used to initiate the drag
18584      */
18585     setOuterHandleElId: function(id) {
18586         if (typeof id !== "string") {
18587             id = Roo.id(id);
18588         }
18589         Event.on(id, "mousedown",
18590                 this.handleMouseDown, this);
18591         this.setHandleElId(id);
18592
18593         this.hasOuterHandles = true;
18594     },
18595
18596     /**
18597      * Remove all drag and drop hooks for this element
18598      * @method unreg
18599      */
18600     unreg: function() {
18601         Event.un(this.id, "mousedown",
18602                 this.handleMouseDown);
18603         Event.un(this.id, "touchstart",
18604                 this.handleMouseDown);
18605         this._domRef = null;
18606         this.DDM._remove(this);
18607     },
18608
18609     destroy : function(){
18610         this.unreg();
18611     },
18612
18613     /**
18614      * Returns true if this instance is locked, or the drag drop mgr is locked
18615      * (meaning that all drag/drop is disabled on the page.)
18616      * @method isLocked
18617      * @return {boolean} true if this obj or all drag/drop is locked, else
18618      * false
18619      */
18620     isLocked: function() {
18621         return (this.DDM.isLocked() || this.locked);
18622     },
18623
18624     /**
18625      * Fired when this object is clicked
18626      * @method handleMouseDown
18627      * @param {Event} e
18628      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18629      * @private
18630      */
18631     handleMouseDown: function(e, oDD){
18632      
18633         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18634             //Roo.log('not touch/ button !=0');
18635             return;
18636         }
18637         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18638             return; // double touch..
18639         }
18640         
18641
18642         if (this.isLocked()) {
18643             //Roo.log('locked');
18644             return;
18645         }
18646
18647         this.DDM.refreshCache(this.groups);
18648 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18649         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18650         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18651             //Roo.log('no outer handes or not over target');
18652                 // do nothing.
18653         } else {
18654 //            Roo.log('check validator');
18655             if (this.clickValidator(e)) {
18656 //                Roo.log('validate success');
18657                 // set the initial element position
18658                 this.setStartPosition();
18659
18660
18661                 this.b4MouseDown(e);
18662                 this.onMouseDown(e);
18663
18664                 this.DDM.handleMouseDown(e, this);
18665
18666                 this.DDM.stopEvent(e);
18667             } else {
18668
18669
18670             }
18671         }
18672     },
18673
18674     clickValidator: function(e) {
18675         var target = e.getTarget();
18676         return ( this.isValidHandleChild(target) &&
18677                     (this.id == this.handleElId ||
18678                         this.DDM.handleWasClicked(target, this.id)) );
18679     },
18680
18681     /**
18682      * Allows you to specify a tag name that should not start a drag operation
18683      * when clicked.  This is designed to facilitate embedding links within a
18684      * drag handle that do something other than start the drag.
18685      * @method addInvalidHandleType
18686      * @param {string} tagName the type of element to exclude
18687      */
18688     addInvalidHandleType: function(tagName) {
18689         var type = tagName.toUpperCase();
18690         this.invalidHandleTypes[type] = type;
18691     },
18692
18693     /**
18694      * Lets you to specify an element id for a child of a drag handle
18695      * that should not initiate a drag
18696      * @method addInvalidHandleId
18697      * @param {string} id the element id of the element you wish to ignore
18698      */
18699     addInvalidHandleId: function(id) {
18700         if (typeof id !== "string") {
18701             id = Roo.id(id);
18702         }
18703         this.invalidHandleIds[id] = id;
18704     },
18705
18706     /**
18707      * Lets you specify a css class of elements that will not initiate a drag
18708      * @method addInvalidHandleClass
18709      * @param {string} cssClass the class of the elements you wish to ignore
18710      */
18711     addInvalidHandleClass: function(cssClass) {
18712         this.invalidHandleClasses.push(cssClass);
18713     },
18714
18715     /**
18716      * Unsets an excluded tag name set by addInvalidHandleType
18717      * @method removeInvalidHandleType
18718      * @param {string} tagName the type of element to unexclude
18719      */
18720     removeInvalidHandleType: function(tagName) {
18721         var type = tagName.toUpperCase();
18722         // this.invalidHandleTypes[type] = null;
18723         delete this.invalidHandleTypes[type];
18724     },
18725
18726     /**
18727      * Unsets an invalid handle id
18728      * @method removeInvalidHandleId
18729      * @param {string} id the id of the element to re-enable
18730      */
18731     removeInvalidHandleId: function(id) {
18732         if (typeof id !== "string") {
18733             id = Roo.id(id);
18734         }
18735         delete this.invalidHandleIds[id];
18736     },
18737
18738     /**
18739      * Unsets an invalid css class
18740      * @method removeInvalidHandleClass
18741      * @param {string} cssClass the class of the element(s) you wish to
18742      * re-enable
18743      */
18744     removeInvalidHandleClass: function(cssClass) {
18745         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18746             if (this.invalidHandleClasses[i] == cssClass) {
18747                 delete this.invalidHandleClasses[i];
18748             }
18749         }
18750     },
18751
18752     /**
18753      * Checks the tag exclusion list to see if this click should be ignored
18754      * @method isValidHandleChild
18755      * @param {HTMLElement} node the HTMLElement to evaluate
18756      * @return {boolean} true if this is a valid tag type, false if not
18757      */
18758     isValidHandleChild: function(node) {
18759
18760         var valid = true;
18761         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18762         var nodeName;
18763         try {
18764             nodeName = node.nodeName.toUpperCase();
18765         } catch(e) {
18766             nodeName = node.nodeName;
18767         }
18768         valid = valid && !this.invalidHandleTypes[nodeName];
18769         valid = valid && !this.invalidHandleIds[node.id];
18770
18771         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18772             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18773         }
18774
18775
18776         return valid;
18777
18778     },
18779
18780     /**
18781      * Create the array of horizontal tick marks if an interval was specified
18782      * in setXConstraint().
18783      * @method setXTicks
18784      * @private
18785      */
18786     setXTicks: function(iStartX, iTickSize) {
18787         this.xTicks = [];
18788         this.xTickSize = iTickSize;
18789
18790         var tickMap = {};
18791
18792         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18793             if (!tickMap[i]) {
18794                 this.xTicks[this.xTicks.length] = i;
18795                 tickMap[i] = true;
18796             }
18797         }
18798
18799         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18800             if (!tickMap[i]) {
18801                 this.xTicks[this.xTicks.length] = i;
18802                 tickMap[i] = true;
18803             }
18804         }
18805
18806         this.xTicks.sort(this.DDM.numericSort) ;
18807     },
18808
18809     /**
18810      * Create the array of vertical tick marks if an interval was specified in
18811      * setYConstraint().
18812      * @method setYTicks
18813      * @private
18814      */
18815     setYTicks: function(iStartY, iTickSize) {
18816         this.yTicks = [];
18817         this.yTickSize = iTickSize;
18818
18819         var tickMap = {};
18820
18821         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18822             if (!tickMap[i]) {
18823                 this.yTicks[this.yTicks.length] = i;
18824                 tickMap[i] = true;
18825             }
18826         }
18827
18828         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18829             if (!tickMap[i]) {
18830                 this.yTicks[this.yTicks.length] = i;
18831                 tickMap[i] = true;
18832             }
18833         }
18834
18835         this.yTicks.sort(this.DDM.numericSort) ;
18836     },
18837
18838     /**
18839      * By default, the element can be dragged any place on the screen.  Use
18840      * this method to limit the horizontal travel of the element.  Pass in
18841      * 0,0 for the parameters if you want to lock the drag to the y axis.
18842      * @method setXConstraint
18843      * @param {int} iLeft the number of pixels the element can move to the left
18844      * @param {int} iRight the number of pixels the element can move to the
18845      * right
18846      * @param {int} iTickSize optional parameter for specifying that the
18847      * element
18848      * should move iTickSize pixels at a time.
18849      */
18850     setXConstraint: function(iLeft, iRight, iTickSize) {
18851         this.leftConstraint = iLeft;
18852         this.rightConstraint = iRight;
18853
18854         this.minX = this.initPageX - iLeft;
18855         this.maxX = this.initPageX + iRight;
18856         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18857
18858         this.constrainX = true;
18859     },
18860
18861     /**
18862      * Clears any constraints applied to this instance.  Also clears ticks
18863      * since they can't exist independent of a constraint at this time.
18864      * @method clearConstraints
18865      */
18866     clearConstraints: function() {
18867         this.constrainX = false;
18868         this.constrainY = false;
18869         this.clearTicks();
18870     },
18871
18872     /**
18873      * Clears any tick interval defined for this instance
18874      * @method clearTicks
18875      */
18876     clearTicks: function() {
18877         this.xTicks = null;
18878         this.yTicks = null;
18879         this.xTickSize = 0;
18880         this.yTickSize = 0;
18881     },
18882
18883     /**
18884      * By default, the element can be dragged any place on the screen.  Set
18885      * this to limit the vertical travel of the element.  Pass in 0,0 for the
18886      * parameters if you want to lock the drag to the x axis.
18887      * @method setYConstraint
18888      * @param {int} iUp the number of pixels the element can move up
18889      * @param {int} iDown the number of pixels the element can move down
18890      * @param {int} iTickSize optional parameter for specifying that the
18891      * element should move iTickSize pixels at a time.
18892      */
18893     setYConstraint: function(iUp, iDown, iTickSize) {
18894         this.topConstraint = iUp;
18895         this.bottomConstraint = iDown;
18896
18897         this.minY = this.initPageY - iUp;
18898         this.maxY = this.initPageY + iDown;
18899         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
18900
18901         this.constrainY = true;
18902
18903     },
18904
18905     /**
18906      * resetConstraints must be called if you manually reposition a dd element.
18907      * @method resetConstraints
18908      * @param {boolean} maintainOffset
18909      */
18910     resetConstraints: function() {
18911
18912
18913         // Maintain offsets if necessary
18914         if (this.initPageX || this.initPageX === 0) {
18915             // figure out how much this thing has moved
18916             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
18917             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
18918
18919             this.setInitPosition(dx, dy);
18920
18921         // This is the first time we have detected the element's position
18922         } else {
18923             this.setInitPosition();
18924         }
18925
18926         if (this.constrainX) {
18927             this.setXConstraint( this.leftConstraint,
18928                                  this.rightConstraint,
18929                                  this.xTickSize        );
18930         }
18931
18932         if (this.constrainY) {
18933             this.setYConstraint( this.topConstraint,
18934                                  this.bottomConstraint,
18935                                  this.yTickSize         );
18936         }
18937     },
18938
18939     /**
18940      * Normally the drag element is moved pixel by pixel, but we can specify
18941      * that it move a number of pixels at a time.  This method resolves the
18942      * location when we have it set up like this.
18943      * @method getTick
18944      * @param {int} val where we want to place the object
18945      * @param {int[]} tickArray sorted array of valid points
18946      * @return {int} the closest tick
18947      * @private
18948      */
18949     getTick: function(val, tickArray) {
18950
18951         if (!tickArray) {
18952             // If tick interval is not defined, it is effectively 1 pixel,
18953             // so we return the value passed to us.
18954             return val;
18955         } else if (tickArray[0] >= val) {
18956             // The value is lower than the first tick, so we return the first
18957             // tick.
18958             return tickArray[0];
18959         } else {
18960             for (var i=0, len=tickArray.length; i<len; ++i) {
18961                 var next = i + 1;
18962                 if (tickArray[next] && tickArray[next] >= val) {
18963                     var diff1 = val - tickArray[i];
18964                     var diff2 = tickArray[next] - val;
18965                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
18966                 }
18967             }
18968
18969             // The value is larger than the last tick, so we return the last
18970             // tick.
18971             return tickArray[tickArray.length - 1];
18972         }
18973     },
18974
18975     /**
18976      * toString method
18977      * @method toString
18978      * @return {string} string representation of the dd obj
18979      */
18980     toString: function() {
18981         return ("DragDrop " + this.id);
18982     }
18983
18984 });
18985
18986 })();
18987 /*
18988  * Based on:
18989  * Ext JS Library 1.1.1
18990  * Copyright(c) 2006-2007, Ext JS, LLC.
18991  *
18992  * Originally Released Under LGPL - original licence link has changed is not relivant.
18993  *
18994  * Fork - LGPL
18995  * <script type="text/javascript">
18996  */
18997
18998
18999 /**
19000  * The drag and drop utility provides a framework for building drag and drop
19001  * applications.  In addition to enabling drag and drop for specific elements,
19002  * the drag and drop elements are tracked by the manager class, and the
19003  * interactions between the various elements are tracked during the drag and
19004  * the implementing code is notified about these important moments.
19005  */
19006
19007 // Only load the library once.  Rewriting the manager class would orphan
19008 // existing drag and drop instances.
19009 if (!Roo.dd.DragDropMgr) {
19010
19011 /**
19012  * @class Roo.dd.DragDropMgr
19013  * DragDropMgr is a singleton that tracks the element interaction for
19014  * all DragDrop items in the window.  Generally, you will not call
19015  * this class directly, but it does have helper methods that could
19016  * be useful in your DragDrop implementations.
19017  * @singleton
19018  */
19019 Roo.dd.DragDropMgr = function() {
19020
19021     var Event = Roo.EventManager;
19022
19023     return {
19024
19025         /**
19026          * Two dimensional Array of registered DragDrop objects.  The first
19027          * dimension is the DragDrop item group, the second the DragDrop
19028          * object.
19029          * @property ids
19030          * @type {string: string}
19031          * @private
19032          * @static
19033          */
19034         ids: {},
19035
19036         /**
19037          * Array of element ids defined as drag handles.  Used to determine
19038          * if the element that generated the mousedown event is actually the
19039          * handle and not the html element itself.
19040          * @property handleIds
19041          * @type {string: string}
19042          * @private
19043          * @static
19044          */
19045         handleIds: {},
19046
19047         /**
19048          * the DragDrop object that is currently being dragged
19049          * @property dragCurrent
19050          * @type DragDrop
19051          * @private
19052          * @static
19053          **/
19054         dragCurrent: null,
19055
19056         /**
19057          * the DragDrop object(s) that are being hovered over
19058          * @property dragOvers
19059          * @type Array
19060          * @private
19061          * @static
19062          */
19063         dragOvers: {},
19064
19065         /**
19066          * the X distance between the cursor and the object being dragged
19067          * @property deltaX
19068          * @type int
19069          * @private
19070          * @static
19071          */
19072         deltaX: 0,
19073
19074         /**
19075          * the Y distance between the cursor and the object being dragged
19076          * @property deltaY
19077          * @type int
19078          * @private
19079          * @static
19080          */
19081         deltaY: 0,
19082
19083         /**
19084          * Flag to determine if we should prevent the default behavior of the
19085          * events we define. By default this is true, but this can be set to
19086          * false if you need the default behavior (not recommended)
19087          * @property preventDefault
19088          * @type boolean
19089          * @static
19090          */
19091         preventDefault: true,
19092
19093         /**
19094          * Flag to determine if we should stop the propagation of the events
19095          * we generate. This is true by default but you may want to set it to
19096          * false if the html element contains other features that require the
19097          * mouse click.
19098          * @property stopPropagation
19099          * @type boolean
19100          * @static
19101          */
19102         stopPropagation: true,
19103
19104         /**
19105          * Internal flag that is set to true when drag and drop has been
19106          * intialized
19107          * @property initialized
19108          * @private
19109          * @static
19110          */
19111         initalized: false,
19112
19113         /**
19114          * All drag and drop can be disabled.
19115          * @property locked
19116          * @private
19117          * @static
19118          */
19119         locked: false,
19120
19121         /**
19122          * Called the first time an element is registered.
19123          * @method init
19124          * @private
19125          * @static
19126          */
19127         init: function() {
19128             this.initialized = true;
19129         },
19130
19131         /**
19132          * In point mode, drag and drop interaction is defined by the
19133          * location of the cursor during the drag/drop
19134          * @property POINT
19135          * @type int
19136          * @static
19137          */
19138         POINT: 0,
19139
19140         /**
19141          * In intersect mode, drag and drop interactio nis defined by the
19142          * overlap of two or more drag and drop objects.
19143          * @property INTERSECT
19144          * @type int
19145          * @static
19146          */
19147         INTERSECT: 1,
19148
19149         /**
19150          * The current drag and drop mode.  Default: POINT
19151          * @property mode
19152          * @type int
19153          * @static
19154          */
19155         mode: 0,
19156
19157         /**
19158          * Runs method on all drag and drop objects
19159          * @method _execOnAll
19160          * @private
19161          * @static
19162          */
19163         _execOnAll: function(sMethod, args) {
19164             for (var i in this.ids) {
19165                 for (var j in this.ids[i]) {
19166                     var oDD = this.ids[i][j];
19167                     if (! this.isTypeOfDD(oDD)) {
19168                         continue;
19169                     }
19170                     oDD[sMethod].apply(oDD, args);
19171                 }
19172             }
19173         },
19174
19175         /**
19176          * Drag and drop initialization.  Sets up the global event handlers
19177          * @method _onLoad
19178          * @private
19179          * @static
19180          */
19181         _onLoad: function() {
19182
19183             this.init();
19184
19185             if (!Roo.isTouch) {
19186                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19187                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19188             }
19189             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19190             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19191             
19192             Event.on(window,   "unload",    this._onUnload, this, true);
19193             Event.on(window,   "resize",    this._onResize, this, true);
19194             // Event.on(window,   "mouseout",    this._test);
19195
19196         },
19197
19198         /**
19199          * Reset constraints on all drag and drop objs
19200          * @method _onResize
19201          * @private
19202          * @static
19203          */
19204         _onResize: function(e) {
19205             this._execOnAll("resetConstraints", []);
19206         },
19207
19208         /**
19209          * Lock all drag and drop functionality
19210          * @method lock
19211          * @static
19212          */
19213         lock: function() { this.locked = true; },
19214
19215         /**
19216          * Unlock all drag and drop functionality
19217          * @method unlock
19218          * @static
19219          */
19220         unlock: function() { this.locked = false; },
19221
19222         /**
19223          * Is drag and drop locked?
19224          * @method isLocked
19225          * @return {boolean} True if drag and drop is locked, false otherwise.
19226          * @static
19227          */
19228         isLocked: function() { return this.locked; },
19229
19230         /**
19231          * Location cache that is set for all drag drop objects when a drag is
19232          * initiated, cleared when the drag is finished.
19233          * @property locationCache
19234          * @private
19235          * @static
19236          */
19237         locationCache: {},
19238
19239         /**
19240          * Set useCache to false if you want to force object the lookup of each
19241          * drag and drop linked element constantly during a drag.
19242          * @property useCache
19243          * @type boolean
19244          * @static
19245          */
19246         useCache: true,
19247
19248         /**
19249          * The number of pixels that the mouse needs to move after the
19250          * mousedown before the drag is initiated.  Default=3;
19251          * @property clickPixelThresh
19252          * @type int
19253          * @static
19254          */
19255         clickPixelThresh: 3,
19256
19257         /**
19258          * The number of milliseconds after the mousedown event to initiate the
19259          * drag if we don't get a mouseup event. Default=1000
19260          * @property clickTimeThresh
19261          * @type int
19262          * @static
19263          */
19264         clickTimeThresh: 350,
19265
19266         /**
19267          * Flag that indicates that either the drag pixel threshold or the
19268          * mousdown time threshold has been met
19269          * @property dragThreshMet
19270          * @type boolean
19271          * @private
19272          * @static
19273          */
19274         dragThreshMet: false,
19275
19276         /**
19277          * Timeout used for the click time threshold
19278          * @property clickTimeout
19279          * @type Object
19280          * @private
19281          * @static
19282          */
19283         clickTimeout: null,
19284
19285         /**
19286          * The X position of the mousedown event stored for later use when a
19287          * drag threshold is met.
19288          * @property startX
19289          * @type int
19290          * @private
19291          * @static
19292          */
19293         startX: 0,
19294
19295         /**
19296          * The Y position of the mousedown event stored for later use when a
19297          * drag threshold is met.
19298          * @property startY
19299          * @type int
19300          * @private
19301          * @static
19302          */
19303         startY: 0,
19304
19305         /**
19306          * Each DragDrop instance must be registered with the DragDropMgr.
19307          * This is executed in DragDrop.init()
19308          * @method regDragDrop
19309          * @param {DragDrop} oDD the DragDrop object to register
19310          * @param {String} sGroup the name of the group this element belongs to
19311          * @static
19312          */
19313         regDragDrop: function(oDD, sGroup) {
19314             if (!this.initialized) { this.init(); }
19315
19316             if (!this.ids[sGroup]) {
19317                 this.ids[sGroup] = {};
19318             }
19319             this.ids[sGroup][oDD.id] = oDD;
19320         },
19321
19322         /**
19323          * Removes the supplied dd instance from the supplied group. Executed
19324          * by DragDrop.removeFromGroup, so don't call this function directly.
19325          * @method removeDDFromGroup
19326          * @private
19327          * @static
19328          */
19329         removeDDFromGroup: function(oDD, sGroup) {
19330             if (!this.ids[sGroup]) {
19331                 this.ids[sGroup] = {};
19332             }
19333
19334             var obj = this.ids[sGroup];
19335             if (obj && obj[oDD.id]) {
19336                 delete obj[oDD.id];
19337             }
19338         },
19339
19340         /**
19341          * Unregisters a drag and drop item.  This is executed in
19342          * DragDrop.unreg, use that method instead of calling this directly.
19343          * @method _remove
19344          * @private
19345          * @static
19346          */
19347         _remove: function(oDD) {
19348             for (var g in oDD.groups) {
19349                 if (g && this.ids[g][oDD.id]) {
19350                     delete this.ids[g][oDD.id];
19351                 }
19352             }
19353             delete this.handleIds[oDD.id];
19354         },
19355
19356         /**
19357          * Each DragDrop handle element must be registered.  This is done
19358          * automatically when executing DragDrop.setHandleElId()
19359          * @method regHandle
19360          * @param {String} sDDId the DragDrop id this element is a handle for
19361          * @param {String} sHandleId the id of the element that is the drag
19362          * handle
19363          * @static
19364          */
19365         regHandle: function(sDDId, sHandleId) {
19366             if (!this.handleIds[sDDId]) {
19367                 this.handleIds[sDDId] = {};
19368             }
19369             this.handleIds[sDDId][sHandleId] = sHandleId;
19370         },
19371
19372         /**
19373          * Utility function to determine if a given element has been
19374          * registered as a drag drop item.
19375          * @method isDragDrop
19376          * @param {String} id the element id to check
19377          * @return {boolean} true if this element is a DragDrop item,
19378          * false otherwise
19379          * @static
19380          */
19381         isDragDrop: function(id) {
19382             return ( this.getDDById(id) ) ? true : false;
19383         },
19384
19385         /**
19386          * Returns the drag and drop instances that are in all groups the
19387          * passed in instance belongs to.
19388          * @method getRelated
19389          * @param {DragDrop} p_oDD the obj to get related data for
19390          * @param {boolean} bTargetsOnly if true, only return targetable objs
19391          * @return {DragDrop[]} the related instances
19392          * @static
19393          */
19394         getRelated: function(p_oDD, bTargetsOnly) {
19395             var oDDs = [];
19396             for (var i in p_oDD.groups) {
19397                 for (j in this.ids[i]) {
19398                     var dd = this.ids[i][j];
19399                     if (! this.isTypeOfDD(dd)) {
19400                         continue;
19401                     }
19402                     if (!bTargetsOnly || dd.isTarget) {
19403                         oDDs[oDDs.length] = dd;
19404                     }
19405                 }
19406             }
19407
19408             return oDDs;
19409         },
19410
19411         /**
19412          * Returns true if the specified dd target is a legal target for
19413          * the specifice drag obj
19414          * @method isLegalTarget
19415          * @param {DragDrop} the drag obj
19416          * @param {DragDrop} the target
19417          * @return {boolean} true if the target is a legal target for the
19418          * dd obj
19419          * @static
19420          */
19421         isLegalTarget: function (oDD, oTargetDD) {
19422             var targets = this.getRelated(oDD, true);
19423             for (var i=0, len=targets.length;i<len;++i) {
19424                 if (targets[i].id == oTargetDD.id) {
19425                     return true;
19426                 }
19427             }
19428
19429             return false;
19430         },
19431
19432         /**
19433          * My goal is to be able to transparently determine if an object is
19434          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19435          * returns "object", oDD.constructor.toString() always returns
19436          * "DragDrop" and not the name of the subclass.  So for now it just
19437          * evaluates a well-known variable in DragDrop.
19438          * @method isTypeOfDD
19439          * @param {Object} the object to evaluate
19440          * @return {boolean} true if typeof oDD = DragDrop
19441          * @static
19442          */
19443         isTypeOfDD: function (oDD) {
19444             return (oDD && oDD.__ygDragDrop);
19445         },
19446
19447         /**
19448          * Utility function to determine if a given element has been
19449          * registered as a drag drop handle for the given Drag Drop object.
19450          * @method isHandle
19451          * @param {String} id the element id to check
19452          * @return {boolean} true if this element is a DragDrop handle, false
19453          * otherwise
19454          * @static
19455          */
19456         isHandle: function(sDDId, sHandleId) {
19457             return ( this.handleIds[sDDId] &&
19458                             this.handleIds[sDDId][sHandleId] );
19459         },
19460
19461         /**
19462          * Returns the DragDrop instance for a given id
19463          * @method getDDById
19464          * @param {String} id the id of the DragDrop object
19465          * @return {DragDrop} the drag drop object, null if it is not found
19466          * @static
19467          */
19468         getDDById: function(id) {
19469             for (var i in this.ids) {
19470                 if (this.ids[i][id]) {
19471                     return this.ids[i][id];
19472                 }
19473             }
19474             return null;
19475         },
19476
19477         /**
19478          * Fired after a registered DragDrop object gets the mousedown event.
19479          * Sets up the events required to track the object being dragged
19480          * @method handleMouseDown
19481          * @param {Event} e the event
19482          * @param oDD the DragDrop object being dragged
19483          * @private
19484          * @static
19485          */
19486         handleMouseDown: function(e, oDD) {
19487             if(Roo.QuickTips){
19488                 Roo.QuickTips.disable();
19489             }
19490             this.currentTarget = e.getTarget();
19491
19492             this.dragCurrent = oDD;
19493
19494             var el = oDD.getEl();
19495
19496             // track start position
19497             this.startX = e.getPageX();
19498             this.startY = e.getPageY();
19499
19500             this.deltaX = this.startX - el.offsetLeft;
19501             this.deltaY = this.startY - el.offsetTop;
19502
19503             this.dragThreshMet = false;
19504
19505             this.clickTimeout = setTimeout(
19506                     function() {
19507                         var DDM = Roo.dd.DDM;
19508                         DDM.startDrag(DDM.startX, DDM.startY);
19509                     },
19510                     this.clickTimeThresh );
19511         },
19512
19513         /**
19514          * Fired when either the drag pixel threshol or the mousedown hold
19515          * time threshold has been met.
19516          * @method startDrag
19517          * @param x {int} the X position of the original mousedown
19518          * @param y {int} the Y position of the original mousedown
19519          * @static
19520          */
19521         startDrag: function(x, y) {
19522             clearTimeout(this.clickTimeout);
19523             if (this.dragCurrent) {
19524                 this.dragCurrent.b4StartDrag(x, y);
19525                 this.dragCurrent.startDrag(x, y);
19526             }
19527             this.dragThreshMet = true;
19528         },
19529
19530         /**
19531          * Internal function to handle the mouseup event.  Will be invoked
19532          * from the context of the document.
19533          * @method handleMouseUp
19534          * @param {Event} e the event
19535          * @private
19536          * @static
19537          */
19538         handleMouseUp: function(e) {
19539
19540             if(Roo.QuickTips){
19541                 Roo.QuickTips.enable();
19542             }
19543             if (! this.dragCurrent) {
19544                 return;
19545             }
19546
19547             clearTimeout(this.clickTimeout);
19548
19549             if (this.dragThreshMet) {
19550                 this.fireEvents(e, true);
19551             } else {
19552             }
19553
19554             this.stopDrag(e);
19555
19556             this.stopEvent(e);
19557         },
19558
19559         /**
19560          * Utility to stop event propagation and event default, if these
19561          * features are turned on.
19562          * @method stopEvent
19563          * @param {Event} e the event as returned by this.getEvent()
19564          * @static
19565          */
19566         stopEvent: function(e){
19567             if(this.stopPropagation) {
19568                 e.stopPropagation();
19569             }
19570
19571             if (this.preventDefault) {
19572                 e.preventDefault();
19573             }
19574         },
19575
19576         /**
19577          * Internal function to clean up event handlers after the drag
19578          * operation is complete
19579          * @method stopDrag
19580          * @param {Event} e the event
19581          * @private
19582          * @static
19583          */
19584         stopDrag: function(e) {
19585             // Fire the drag end event for the item that was dragged
19586             if (this.dragCurrent) {
19587                 if (this.dragThreshMet) {
19588                     this.dragCurrent.b4EndDrag(e);
19589                     this.dragCurrent.endDrag(e);
19590                 }
19591
19592                 this.dragCurrent.onMouseUp(e);
19593             }
19594
19595             this.dragCurrent = null;
19596             this.dragOvers = {};
19597         },
19598
19599         /**
19600          * Internal function to handle the mousemove event.  Will be invoked
19601          * from the context of the html element.
19602          *
19603          * @TODO figure out what we can do about mouse events lost when the
19604          * user drags objects beyond the window boundary.  Currently we can
19605          * detect this in internet explorer by verifying that the mouse is
19606          * down during the mousemove event.  Firefox doesn't give us the
19607          * button state on the mousemove event.
19608          * @method handleMouseMove
19609          * @param {Event} e the event
19610          * @private
19611          * @static
19612          */
19613         handleMouseMove: function(e) {
19614             if (! this.dragCurrent) {
19615                 return true;
19616             }
19617
19618             // var button = e.which || e.button;
19619
19620             // check for IE mouseup outside of page boundary
19621             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19622                 this.stopEvent(e);
19623                 return this.handleMouseUp(e);
19624             }
19625
19626             if (!this.dragThreshMet) {
19627                 var diffX = Math.abs(this.startX - e.getPageX());
19628                 var diffY = Math.abs(this.startY - e.getPageY());
19629                 if (diffX > this.clickPixelThresh ||
19630                             diffY > this.clickPixelThresh) {
19631                     this.startDrag(this.startX, this.startY);
19632                 }
19633             }
19634
19635             if (this.dragThreshMet) {
19636                 this.dragCurrent.b4Drag(e);
19637                 this.dragCurrent.onDrag(e);
19638                 if(!this.dragCurrent.moveOnly){
19639                     this.fireEvents(e, false);
19640                 }
19641             }
19642
19643             this.stopEvent(e);
19644
19645             return true;
19646         },
19647
19648         /**
19649          * Iterates over all of the DragDrop elements to find ones we are
19650          * hovering over or dropping on
19651          * @method fireEvents
19652          * @param {Event} e the event
19653          * @param {boolean} isDrop is this a drop op or a mouseover op?
19654          * @private
19655          * @static
19656          */
19657         fireEvents: function(e, isDrop) {
19658             var dc = this.dragCurrent;
19659
19660             // If the user did the mouse up outside of the window, we could
19661             // get here even though we have ended the drag.
19662             if (!dc || dc.isLocked()) {
19663                 return;
19664             }
19665
19666             var pt = e.getPoint();
19667
19668             // cache the previous dragOver array
19669             var oldOvers = [];
19670
19671             var outEvts   = [];
19672             var overEvts  = [];
19673             var dropEvts  = [];
19674             var enterEvts = [];
19675
19676             // Check to see if the object(s) we were hovering over is no longer
19677             // being hovered over so we can fire the onDragOut event
19678             for (var i in this.dragOvers) {
19679
19680                 var ddo = this.dragOvers[i];
19681
19682                 if (! this.isTypeOfDD(ddo)) {
19683                     continue;
19684                 }
19685
19686                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19687                     outEvts.push( ddo );
19688                 }
19689
19690                 oldOvers[i] = true;
19691                 delete this.dragOvers[i];
19692             }
19693
19694             for (var sGroup in dc.groups) {
19695
19696                 if ("string" != typeof sGroup) {
19697                     continue;
19698                 }
19699
19700                 for (i in this.ids[sGroup]) {
19701                     var oDD = this.ids[sGroup][i];
19702                     if (! this.isTypeOfDD(oDD)) {
19703                         continue;
19704                     }
19705
19706                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19707                         if (this.isOverTarget(pt, oDD, this.mode)) {
19708                             // look for drop interactions
19709                             if (isDrop) {
19710                                 dropEvts.push( oDD );
19711                             // look for drag enter and drag over interactions
19712                             } else {
19713
19714                                 // initial drag over: dragEnter fires
19715                                 if (!oldOvers[oDD.id]) {
19716                                     enterEvts.push( oDD );
19717                                 // subsequent drag overs: dragOver fires
19718                                 } else {
19719                                     overEvts.push( oDD );
19720                                 }
19721
19722                                 this.dragOvers[oDD.id] = oDD;
19723                             }
19724                         }
19725                     }
19726                 }
19727             }
19728
19729             if (this.mode) {
19730                 if (outEvts.length) {
19731                     dc.b4DragOut(e, outEvts);
19732                     dc.onDragOut(e, outEvts);
19733                 }
19734
19735                 if (enterEvts.length) {
19736                     dc.onDragEnter(e, enterEvts);
19737                 }
19738
19739                 if (overEvts.length) {
19740                     dc.b4DragOver(e, overEvts);
19741                     dc.onDragOver(e, overEvts);
19742                 }
19743
19744                 if (dropEvts.length) {
19745                     dc.b4DragDrop(e, dropEvts);
19746                     dc.onDragDrop(e, dropEvts);
19747                 }
19748
19749             } else {
19750                 // fire dragout events
19751                 var len = 0;
19752                 for (i=0, len=outEvts.length; i<len; ++i) {
19753                     dc.b4DragOut(e, outEvts[i].id);
19754                     dc.onDragOut(e, outEvts[i].id);
19755                 }
19756
19757                 // fire enter events
19758                 for (i=0,len=enterEvts.length; i<len; ++i) {
19759                     // dc.b4DragEnter(e, oDD.id);
19760                     dc.onDragEnter(e, enterEvts[i].id);
19761                 }
19762
19763                 // fire over events
19764                 for (i=0,len=overEvts.length; i<len; ++i) {
19765                     dc.b4DragOver(e, overEvts[i].id);
19766                     dc.onDragOver(e, overEvts[i].id);
19767                 }
19768
19769                 // fire drop events
19770                 for (i=0, len=dropEvts.length; i<len; ++i) {
19771                     dc.b4DragDrop(e, dropEvts[i].id);
19772                     dc.onDragDrop(e, dropEvts[i].id);
19773                 }
19774
19775             }
19776
19777             // notify about a drop that did not find a target
19778             if (isDrop && !dropEvts.length) {
19779                 dc.onInvalidDrop(e);
19780             }
19781
19782         },
19783
19784         /**
19785          * Helper function for getting the best match from the list of drag
19786          * and drop objects returned by the drag and drop events when we are
19787          * in INTERSECT mode.  It returns either the first object that the
19788          * cursor is over, or the object that has the greatest overlap with
19789          * the dragged element.
19790          * @method getBestMatch
19791          * @param  {DragDrop[]} dds The array of drag and drop objects
19792          * targeted
19793          * @return {DragDrop}       The best single match
19794          * @static
19795          */
19796         getBestMatch: function(dds) {
19797             var winner = null;
19798             // Return null if the input is not what we expect
19799             //if (!dds || !dds.length || dds.length == 0) {
19800                // winner = null;
19801             // If there is only one item, it wins
19802             //} else if (dds.length == 1) {
19803
19804             var len = dds.length;
19805
19806             if (len == 1) {
19807                 winner = dds[0];
19808             } else {
19809                 // Loop through the targeted items
19810                 for (var i=0; i<len; ++i) {
19811                     var dd = dds[i];
19812                     // If the cursor is over the object, it wins.  If the
19813                     // cursor is over multiple matches, the first one we come
19814                     // to wins.
19815                     if (dd.cursorIsOver) {
19816                         winner = dd;
19817                         break;
19818                     // Otherwise the object with the most overlap wins
19819                     } else {
19820                         if (!winner ||
19821                             winner.overlap.getArea() < dd.overlap.getArea()) {
19822                             winner = dd;
19823                         }
19824                     }
19825                 }
19826             }
19827
19828             return winner;
19829         },
19830
19831         /**
19832          * Refreshes the cache of the top-left and bottom-right points of the
19833          * drag and drop objects in the specified group(s).  This is in the
19834          * format that is stored in the drag and drop instance, so typical
19835          * usage is:
19836          * <code>
19837          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19838          * </code>
19839          * Alternatively:
19840          * <code>
19841          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19842          * </code>
19843          * @TODO this really should be an indexed array.  Alternatively this
19844          * method could accept both.
19845          * @method refreshCache
19846          * @param {Object} groups an associative array of groups to refresh
19847          * @static
19848          */
19849         refreshCache: function(groups) {
19850             for (var sGroup in groups) {
19851                 if ("string" != typeof sGroup) {
19852                     continue;
19853                 }
19854                 for (var i in this.ids[sGroup]) {
19855                     var oDD = this.ids[sGroup][i];
19856
19857                     if (this.isTypeOfDD(oDD)) {
19858                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19859                         var loc = this.getLocation(oDD);
19860                         if (loc) {
19861                             this.locationCache[oDD.id] = loc;
19862                         } else {
19863                             delete this.locationCache[oDD.id];
19864                             // this will unregister the drag and drop object if
19865                             // the element is not in a usable state
19866                             // oDD.unreg();
19867                         }
19868                     }
19869                 }
19870             }
19871         },
19872
19873         /**
19874          * This checks to make sure an element exists and is in the DOM.  The
19875          * main purpose is to handle cases where innerHTML is used to remove
19876          * drag and drop objects from the DOM.  IE provides an 'unspecified
19877          * error' when trying to access the offsetParent of such an element
19878          * @method verifyEl
19879          * @param {HTMLElement} el the element to check
19880          * @return {boolean} true if the element looks usable
19881          * @static
19882          */
19883         verifyEl: function(el) {
19884             if (el) {
19885                 var parent;
19886                 if(Roo.isIE){
19887                     try{
19888                         parent = el.offsetParent;
19889                     }catch(e){}
19890                 }else{
19891                     parent = el.offsetParent;
19892                 }
19893                 if (parent) {
19894                     return true;
19895                 }
19896             }
19897
19898             return false;
19899         },
19900
19901         /**
19902          * Returns a Region object containing the drag and drop element's position
19903          * and size, including the padding configured for it
19904          * @method getLocation
19905          * @param {DragDrop} oDD the drag and drop object to get the
19906          *                       location for
19907          * @return {Roo.lib.Region} a Region object representing the total area
19908          *                             the element occupies, including any padding
19909          *                             the instance is configured for.
19910          * @static
19911          */
19912         getLocation: function(oDD) {
19913             if (! this.isTypeOfDD(oDD)) {
19914                 return null;
19915             }
19916
19917             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
19918
19919             try {
19920                 pos= Roo.lib.Dom.getXY(el);
19921             } catch (e) { }
19922
19923             if (!pos) {
19924                 return null;
19925             }
19926
19927             x1 = pos[0];
19928             x2 = x1 + el.offsetWidth;
19929             y1 = pos[1];
19930             y2 = y1 + el.offsetHeight;
19931
19932             t = y1 - oDD.padding[0];
19933             r = x2 + oDD.padding[1];
19934             b = y2 + oDD.padding[2];
19935             l = x1 - oDD.padding[3];
19936
19937             return new Roo.lib.Region( t, r, b, l );
19938         },
19939
19940         /**
19941          * Checks the cursor location to see if it over the target
19942          * @method isOverTarget
19943          * @param {Roo.lib.Point} pt The point to evaluate
19944          * @param {DragDrop} oTarget the DragDrop object we are inspecting
19945          * @return {boolean} true if the mouse is over the target
19946          * @private
19947          * @static
19948          */
19949         isOverTarget: function(pt, oTarget, intersect) {
19950             // use cache if available
19951             var loc = this.locationCache[oTarget.id];
19952             if (!loc || !this.useCache) {
19953                 loc = this.getLocation(oTarget);
19954                 this.locationCache[oTarget.id] = loc;
19955
19956             }
19957
19958             if (!loc) {
19959                 return false;
19960             }
19961
19962             oTarget.cursorIsOver = loc.contains( pt );
19963
19964             // DragDrop is using this as a sanity check for the initial mousedown
19965             // in this case we are done.  In POINT mode, if the drag obj has no
19966             // contraints, we are also done. Otherwise we need to evaluate the
19967             // location of the target as related to the actual location of the
19968             // dragged element.
19969             var dc = this.dragCurrent;
19970             if (!dc || !dc.getTargetCoord ||
19971                     (!intersect && !dc.constrainX && !dc.constrainY)) {
19972                 return oTarget.cursorIsOver;
19973             }
19974
19975             oTarget.overlap = null;
19976
19977             // Get the current location of the drag element, this is the
19978             // location of the mouse event less the delta that represents
19979             // where the original mousedown happened on the element.  We
19980             // need to consider constraints and ticks as well.
19981             var pos = dc.getTargetCoord(pt.x, pt.y);
19982
19983             var el = dc.getDragEl();
19984             var curRegion = new Roo.lib.Region( pos.y,
19985                                                    pos.x + el.offsetWidth,
19986                                                    pos.y + el.offsetHeight,
19987                                                    pos.x );
19988
19989             var overlap = curRegion.intersect(loc);
19990
19991             if (overlap) {
19992                 oTarget.overlap = overlap;
19993                 return (intersect) ? true : oTarget.cursorIsOver;
19994             } else {
19995                 return false;
19996             }
19997         },
19998
19999         /**
20000          * unload event handler
20001          * @method _onUnload
20002          * @private
20003          * @static
20004          */
20005         _onUnload: function(e, me) {
20006             Roo.dd.DragDropMgr.unregAll();
20007         },
20008
20009         /**
20010          * Cleans up the drag and drop events and objects.
20011          * @method unregAll
20012          * @private
20013          * @static
20014          */
20015         unregAll: function() {
20016
20017             if (this.dragCurrent) {
20018                 this.stopDrag();
20019                 this.dragCurrent = null;
20020             }
20021
20022             this._execOnAll("unreg", []);
20023
20024             for (i in this.elementCache) {
20025                 delete this.elementCache[i];
20026             }
20027
20028             this.elementCache = {};
20029             this.ids = {};
20030         },
20031
20032         /**
20033          * A cache of DOM elements
20034          * @property elementCache
20035          * @private
20036          * @static
20037          */
20038         elementCache: {},
20039
20040         /**
20041          * Get the wrapper for the DOM element specified
20042          * @method getElWrapper
20043          * @param {String} id the id of the element to get
20044          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20045          * @private
20046          * @deprecated This wrapper isn't that useful
20047          * @static
20048          */
20049         getElWrapper: function(id) {
20050             var oWrapper = this.elementCache[id];
20051             if (!oWrapper || !oWrapper.el) {
20052                 oWrapper = this.elementCache[id] =
20053                     new this.ElementWrapper(Roo.getDom(id));
20054             }
20055             return oWrapper;
20056         },
20057
20058         /**
20059          * Returns the actual DOM element
20060          * @method getElement
20061          * @param {String} id the id of the elment to get
20062          * @return {Object} The element
20063          * @deprecated use Roo.getDom instead
20064          * @static
20065          */
20066         getElement: function(id) {
20067             return Roo.getDom(id);
20068         },
20069
20070         /**
20071          * Returns the style property for the DOM element (i.e.,
20072          * document.getElById(id).style)
20073          * @method getCss
20074          * @param {String} id the id of the elment to get
20075          * @return {Object} The style property of the element
20076          * @deprecated use Roo.getDom instead
20077          * @static
20078          */
20079         getCss: function(id) {
20080             var el = Roo.getDom(id);
20081             return (el) ? el.style : null;
20082         },
20083
20084         /**
20085          * Inner class for cached elements
20086          * @class DragDropMgr.ElementWrapper
20087          * @for DragDropMgr
20088          * @private
20089          * @deprecated
20090          */
20091         ElementWrapper: function(el) {
20092                 /**
20093                  * The element
20094                  * @property el
20095                  */
20096                 this.el = el || null;
20097                 /**
20098                  * The element id
20099                  * @property id
20100                  */
20101                 this.id = this.el && el.id;
20102                 /**
20103                  * A reference to the style property
20104                  * @property css
20105                  */
20106                 this.css = this.el && el.style;
20107             },
20108
20109         /**
20110          * Returns the X position of an html element
20111          * @method getPosX
20112          * @param el the element for which to get the position
20113          * @return {int} the X coordinate
20114          * @for DragDropMgr
20115          * @deprecated use Roo.lib.Dom.getX instead
20116          * @static
20117          */
20118         getPosX: function(el) {
20119             return Roo.lib.Dom.getX(el);
20120         },
20121
20122         /**
20123          * Returns the Y position of an html element
20124          * @method getPosY
20125          * @param el the element for which to get the position
20126          * @return {int} the Y coordinate
20127          * @deprecated use Roo.lib.Dom.getY instead
20128          * @static
20129          */
20130         getPosY: function(el) {
20131             return Roo.lib.Dom.getY(el);
20132         },
20133
20134         /**
20135          * Swap two nodes.  In IE, we use the native method, for others we
20136          * emulate the IE behavior
20137          * @method swapNode
20138          * @param n1 the first node to swap
20139          * @param n2 the other node to swap
20140          * @static
20141          */
20142         swapNode: function(n1, n2) {
20143             if (n1.swapNode) {
20144                 n1.swapNode(n2);
20145             } else {
20146                 var p = n2.parentNode;
20147                 var s = n2.nextSibling;
20148
20149                 if (s == n1) {
20150                     p.insertBefore(n1, n2);
20151                 } else if (n2 == n1.nextSibling) {
20152                     p.insertBefore(n2, n1);
20153                 } else {
20154                     n1.parentNode.replaceChild(n2, n1);
20155                     p.insertBefore(n1, s);
20156                 }
20157             }
20158         },
20159
20160         /**
20161          * Returns the current scroll position
20162          * @method getScroll
20163          * @private
20164          * @static
20165          */
20166         getScroll: function () {
20167             var t, l, dde=document.documentElement, db=document.body;
20168             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20169                 t = dde.scrollTop;
20170                 l = dde.scrollLeft;
20171             } else if (db) {
20172                 t = db.scrollTop;
20173                 l = db.scrollLeft;
20174             } else {
20175
20176             }
20177             return { top: t, left: l };
20178         },
20179
20180         /**
20181          * Returns the specified element style property
20182          * @method getStyle
20183          * @param {HTMLElement} el          the element
20184          * @param {string}      styleProp   the style property
20185          * @return {string} The value of the style property
20186          * @deprecated use Roo.lib.Dom.getStyle
20187          * @static
20188          */
20189         getStyle: function(el, styleProp) {
20190             return Roo.fly(el).getStyle(styleProp);
20191         },
20192
20193         /**
20194          * Gets the scrollTop
20195          * @method getScrollTop
20196          * @return {int} the document's scrollTop
20197          * @static
20198          */
20199         getScrollTop: function () { return this.getScroll().top; },
20200
20201         /**
20202          * Gets the scrollLeft
20203          * @method getScrollLeft
20204          * @return {int} the document's scrollTop
20205          * @static
20206          */
20207         getScrollLeft: function () { return this.getScroll().left; },
20208
20209         /**
20210          * Sets the x/y position of an element to the location of the
20211          * target element.
20212          * @method moveToEl
20213          * @param {HTMLElement} moveEl      The element to move
20214          * @param {HTMLElement} targetEl    The position reference element
20215          * @static
20216          */
20217         moveToEl: function (moveEl, targetEl) {
20218             var aCoord = Roo.lib.Dom.getXY(targetEl);
20219             Roo.lib.Dom.setXY(moveEl, aCoord);
20220         },
20221
20222         /**
20223          * Numeric array sort function
20224          * @method numericSort
20225          * @static
20226          */
20227         numericSort: function(a, b) { return (a - b); },
20228
20229         /**
20230          * Internal counter
20231          * @property _timeoutCount
20232          * @private
20233          * @static
20234          */
20235         _timeoutCount: 0,
20236
20237         /**
20238          * Trying to make the load order less important.  Without this we get
20239          * an error if this file is loaded before the Event Utility.
20240          * @method _addListeners
20241          * @private
20242          * @static
20243          */
20244         _addListeners: function() {
20245             var DDM = Roo.dd.DDM;
20246             if ( Roo.lib.Event && document ) {
20247                 DDM._onLoad();
20248             } else {
20249                 if (DDM._timeoutCount > 2000) {
20250                 } else {
20251                     setTimeout(DDM._addListeners, 10);
20252                     if (document && document.body) {
20253                         DDM._timeoutCount += 1;
20254                     }
20255                 }
20256             }
20257         },
20258
20259         /**
20260          * Recursively searches the immediate parent and all child nodes for
20261          * the handle element in order to determine wheter or not it was
20262          * clicked.
20263          * @method handleWasClicked
20264          * @param node the html element to inspect
20265          * @static
20266          */
20267         handleWasClicked: function(node, id) {
20268             if (this.isHandle(id, node.id)) {
20269                 return true;
20270             } else {
20271                 // check to see if this is a text node child of the one we want
20272                 var p = node.parentNode;
20273
20274                 while (p) {
20275                     if (this.isHandle(id, p.id)) {
20276                         return true;
20277                     } else {
20278                         p = p.parentNode;
20279                     }
20280                 }
20281             }
20282
20283             return false;
20284         }
20285
20286     };
20287
20288 }();
20289
20290 // shorter alias, save a few bytes
20291 Roo.dd.DDM = Roo.dd.DragDropMgr;
20292 Roo.dd.DDM._addListeners();
20293
20294 }/*
20295  * Based on:
20296  * Ext JS Library 1.1.1
20297  * Copyright(c) 2006-2007, Ext JS, LLC.
20298  *
20299  * Originally Released Under LGPL - original licence link has changed is not relivant.
20300  *
20301  * Fork - LGPL
20302  * <script type="text/javascript">
20303  */
20304
20305 /**
20306  * @class Roo.dd.DD
20307  * A DragDrop implementation where the linked element follows the
20308  * mouse cursor during a drag.
20309  * @extends Roo.dd.DragDrop
20310  * @constructor
20311  * @param {String} id the id of the linked element
20312  * @param {String} sGroup the group of related DragDrop items
20313  * @param {object} config an object containing configurable attributes
20314  *                Valid properties for DD:
20315  *                    scroll
20316  */
20317 Roo.dd.DD = function(id, sGroup, config) {
20318     if (id) {
20319         this.init(id, sGroup, config);
20320     }
20321 };
20322
20323 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20324
20325     /**
20326      * When set to true, the utility automatically tries to scroll the browser
20327      * window wehn a drag and drop element is dragged near the viewport boundary.
20328      * Defaults to true.
20329      * @property scroll
20330      * @type boolean
20331      */
20332     scroll: true,
20333
20334     /**
20335      * Sets the pointer offset to the distance between the linked element's top
20336      * left corner and the location the element was clicked
20337      * @method autoOffset
20338      * @param {int} iPageX the X coordinate of the click
20339      * @param {int} iPageY the Y coordinate of the click
20340      */
20341     autoOffset: function(iPageX, iPageY) {
20342         var x = iPageX - this.startPageX;
20343         var y = iPageY - this.startPageY;
20344         this.setDelta(x, y);
20345     },
20346
20347     /**
20348      * Sets the pointer offset.  You can call this directly to force the
20349      * offset to be in a particular location (e.g., pass in 0,0 to set it
20350      * to the center of the object)
20351      * @method setDelta
20352      * @param {int} iDeltaX the distance from the left
20353      * @param {int} iDeltaY the distance from the top
20354      */
20355     setDelta: function(iDeltaX, iDeltaY) {
20356         this.deltaX = iDeltaX;
20357         this.deltaY = iDeltaY;
20358     },
20359
20360     /**
20361      * Sets the drag element to the location of the mousedown or click event,
20362      * maintaining the cursor location relative to the location on the element
20363      * that was clicked.  Override this if you want to place the element in a
20364      * location other than where the cursor is.
20365      * @method setDragElPos
20366      * @param {int} iPageX the X coordinate of the mousedown or drag event
20367      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20368      */
20369     setDragElPos: function(iPageX, iPageY) {
20370         // the first time we do this, we are going to check to make sure
20371         // the element has css positioning
20372
20373         var el = this.getDragEl();
20374         this.alignElWithMouse(el, iPageX, iPageY);
20375     },
20376
20377     /**
20378      * Sets the element to the location of the mousedown or click event,
20379      * maintaining the cursor location relative to the location on the element
20380      * that was clicked.  Override this if you want to place the element in a
20381      * location other than where the cursor is.
20382      * @method alignElWithMouse
20383      * @param {HTMLElement} el the element to move
20384      * @param {int} iPageX the X coordinate of the mousedown or drag event
20385      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20386      */
20387     alignElWithMouse: function(el, iPageX, iPageY) {
20388         var oCoord = this.getTargetCoord(iPageX, iPageY);
20389         var fly = el.dom ? el : Roo.fly(el);
20390         if (!this.deltaSetXY) {
20391             var aCoord = [oCoord.x, oCoord.y];
20392             fly.setXY(aCoord);
20393             var newLeft = fly.getLeft(true);
20394             var newTop  = fly.getTop(true);
20395             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20396         } else {
20397             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20398         }
20399
20400         this.cachePosition(oCoord.x, oCoord.y);
20401         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20402         return oCoord;
20403     },
20404
20405     /**
20406      * Saves the most recent position so that we can reset the constraints and
20407      * tick marks on-demand.  We need to know this so that we can calculate the
20408      * number of pixels the element is offset from its original position.
20409      * @method cachePosition
20410      * @param iPageX the current x position (optional, this just makes it so we
20411      * don't have to look it up again)
20412      * @param iPageY the current y position (optional, this just makes it so we
20413      * don't have to look it up again)
20414      */
20415     cachePosition: function(iPageX, iPageY) {
20416         if (iPageX) {
20417             this.lastPageX = iPageX;
20418             this.lastPageY = iPageY;
20419         } else {
20420             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20421             this.lastPageX = aCoord[0];
20422             this.lastPageY = aCoord[1];
20423         }
20424     },
20425
20426     /**
20427      * Auto-scroll the window if the dragged object has been moved beyond the
20428      * visible window boundary.
20429      * @method autoScroll
20430      * @param {int} x the drag element's x position
20431      * @param {int} y the drag element's y position
20432      * @param {int} h the height of the drag element
20433      * @param {int} w the width of the drag element
20434      * @private
20435      */
20436     autoScroll: function(x, y, h, w) {
20437
20438         if (this.scroll) {
20439             // The client height
20440             var clientH = Roo.lib.Dom.getViewWidth();
20441
20442             // The client width
20443             var clientW = Roo.lib.Dom.getViewHeight();
20444
20445             // The amt scrolled down
20446             var st = this.DDM.getScrollTop();
20447
20448             // The amt scrolled right
20449             var sl = this.DDM.getScrollLeft();
20450
20451             // Location of the bottom of the element
20452             var bot = h + y;
20453
20454             // Location of the right of the element
20455             var right = w + x;
20456
20457             // The distance from the cursor to the bottom of the visible area,
20458             // adjusted so that we don't scroll if the cursor is beyond the
20459             // element drag constraints
20460             var toBot = (clientH + st - y - this.deltaY);
20461
20462             // The distance from the cursor to the right of the visible area
20463             var toRight = (clientW + sl - x - this.deltaX);
20464
20465
20466             // How close to the edge the cursor must be before we scroll
20467             // var thresh = (document.all) ? 100 : 40;
20468             var thresh = 40;
20469
20470             // How many pixels to scroll per autoscroll op.  This helps to reduce
20471             // clunky scrolling. IE is more sensitive about this ... it needs this
20472             // value to be higher.
20473             var scrAmt = (document.all) ? 80 : 30;
20474
20475             // Scroll down if we are near the bottom of the visible page and the
20476             // obj extends below the crease
20477             if ( bot > clientH && toBot < thresh ) {
20478                 window.scrollTo(sl, st + scrAmt);
20479             }
20480
20481             // Scroll up if the window is scrolled down and the top of the object
20482             // goes above the top border
20483             if ( y < st && st > 0 && y - st < thresh ) {
20484                 window.scrollTo(sl, st - scrAmt);
20485             }
20486
20487             // Scroll right if the obj is beyond the right border and the cursor is
20488             // near the border.
20489             if ( right > clientW && toRight < thresh ) {
20490                 window.scrollTo(sl + scrAmt, st);
20491             }
20492
20493             // Scroll left if the window has been scrolled to the right and the obj
20494             // extends past the left border
20495             if ( x < sl && sl > 0 && x - sl < thresh ) {
20496                 window.scrollTo(sl - scrAmt, st);
20497             }
20498         }
20499     },
20500
20501     /**
20502      * Finds the location the element should be placed if we want to move
20503      * it to where the mouse location less the click offset would place us.
20504      * @method getTargetCoord
20505      * @param {int} iPageX the X coordinate of the click
20506      * @param {int} iPageY the Y coordinate of the click
20507      * @return an object that contains the coordinates (Object.x and Object.y)
20508      * @private
20509      */
20510     getTargetCoord: function(iPageX, iPageY) {
20511
20512
20513         var x = iPageX - this.deltaX;
20514         var y = iPageY - this.deltaY;
20515
20516         if (this.constrainX) {
20517             if (x < this.minX) { x = this.minX; }
20518             if (x > this.maxX) { x = this.maxX; }
20519         }
20520
20521         if (this.constrainY) {
20522             if (y < this.minY) { y = this.minY; }
20523             if (y > this.maxY) { y = this.maxY; }
20524         }
20525
20526         x = this.getTick(x, this.xTicks);
20527         y = this.getTick(y, this.yTicks);
20528
20529
20530         return {x:x, y:y};
20531     },
20532
20533     /*
20534      * Sets up config options specific to this class. Overrides
20535      * Roo.dd.DragDrop, but all versions of this method through the
20536      * inheritance chain are called
20537      */
20538     applyConfig: function() {
20539         Roo.dd.DD.superclass.applyConfig.call(this);
20540         this.scroll = (this.config.scroll !== false);
20541     },
20542
20543     /*
20544      * Event that fires prior to the onMouseDown event.  Overrides
20545      * Roo.dd.DragDrop.
20546      */
20547     b4MouseDown: function(e) {
20548         // this.resetConstraints();
20549         this.autoOffset(e.getPageX(),
20550                             e.getPageY());
20551     },
20552
20553     /*
20554      * Event that fires prior to the onDrag event.  Overrides
20555      * Roo.dd.DragDrop.
20556      */
20557     b4Drag: function(e) {
20558         this.setDragElPos(e.getPageX(),
20559                             e.getPageY());
20560     },
20561
20562     toString: function() {
20563         return ("DD " + this.id);
20564     }
20565
20566     //////////////////////////////////////////////////////////////////////////
20567     // Debugging ygDragDrop events that can be overridden
20568     //////////////////////////////////////////////////////////////////////////
20569     /*
20570     startDrag: function(x, y) {
20571     },
20572
20573     onDrag: function(e) {
20574     },
20575
20576     onDragEnter: function(e, id) {
20577     },
20578
20579     onDragOver: function(e, id) {
20580     },
20581
20582     onDragOut: function(e, id) {
20583     },
20584
20585     onDragDrop: function(e, id) {
20586     },
20587
20588     endDrag: function(e) {
20589     }
20590
20591     */
20592
20593 });/*
20594  * Based on:
20595  * Ext JS Library 1.1.1
20596  * Copyright(c) 2006-2007, Ext JS, LLC.
20597  *
20598  * Originally Released Under LGPL - original licence link has changed is not relivant.
20599  *
20600  * Fork - LGPL
20601  * <script type="text/javascript">
20602  */
20603
20604 /**
20605  * @class Roo.dd.DDProxy
20606  * A DragDrop implementation that inserts an empty, bordered div into
20607  * the document that follows the cursor during drag operations.  At the time of
20608  * the click, the frame div is resized to the dimensions of the linked html
20609  * element, and moved to the exact location of the linked element.
20610  *
20611  * References to the "frame" element refer to the single proxy element that
20612  * was created to be dragged in place of all DDProxy elements on the
20613  * page.
20614  *
20615  * @extends Roo.dd.DD
20616  * @constructor
20617  * @param {String} id the id of the linked html element
20618  * @param {String} sGroup the group of related DragDrop objects
20619  * @param {object} config an object containing configurable attributes
20620  *                Valid properties for DDProxy in addition to those in DragDrop:
20621  *                   resizeFrame, centerFrame, dragElId
20622  */
20623 Roo.dd.DDProxy = function(id, sGroup, config) {
20624     if (id) {
20625         this.init(id, sGroup, config);
20626         this.initFrame();
20627     }
20628 };
20629
20630 /**
20631  * The default drag frame div id
20632  * @property Roo.dd.DDProxy.dragElId
20633  * @type String
20634  * @static
20635  */
20636 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20637
20638 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20639
20640     /**
20641      * By default we resize the drag frame to be the same size as the element
20642      * we want to drag (this is to get the frame effect).  We can turn it off
20643      * if we want a different behavior.
20644      * @property resizeFrame
20645      * @type boolean
20646      */
20647     resizeFrame: true,
20648
20649     /**
20650      * By default the frame is positioned exactly where the drag element is, so
20651      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20652      * you do not have constraints on the obj is to have the drag frame centered
20653      * around the cursor.  Set centerFrame to true for this effect.
20654      * @property centerFrame
20655      * @type boolean
20656      */
20657     centerFrame: false,
20658
20659     /**
20660      * Creates the proxy element if it does not yet exist
20661      * @method createFrame
20662      */
20663     createFrame: function() {
20664         var self = this;
20665         var body = document.body;
20666
20667         if (!body || !body.firstChild) {
20668             setTimeout( function() { self.createFrame(); }, 50 );
20669             return;
20670         }
20671
20672         var div = this.getDragEl();
20673
20674         if (!div) {
20675             div    = document.createElement("div");
20676             div.id = this.dragElId;
20677             var s  = div.style;
20678
20679             s.position   = "absolute";
20680             s.visibility = "hidden";
20681             s.cursor     = "move";
20682             s.border     = "2px solid #aaa";
20683             s.zIndex     = 999;
20684
20685             // appendChild can blow up IE if invoked prior to the window load event
20686             // while rendering a table.  It is possible there are other scenarios
20687             // that would cause this to happen as well.
20688             body.insertBefore(div, body.firstChild);
20689         }
20690     },
20691
20692     /**
20693      * Initialization for the drag frame element.  Must be called in the
20694      * constructor of all subclasses
20695      * @method initFrame
20696      */
20697     initFrame: function() {
20698         this.createFrame();
20699     },
20700
20701     applyConfig: function() {
20702         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20703
20704         this.resizeFrame = (this.config.resizeFrame !== false);
20705         this.centerFrame = (this.config.centerFrame);
20706         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20707     },
20708
20709     /**
20710      * Resizes the drag frame to the dimensions of the clicked object, positions
20711      * it over the object, and finally displays it
20712      * @method showFrame
20713      * @param {int} iPageX X click position
20714      * @param {int} iPageY Y click position
20715      * @private
20716      */
20717     showFrame: function(iPageX, iPageY) {
20718         var el = this.getEl();
20719         var dragEl = this.getDragEl();
20720         var s = dragEl.style;
20721
20722         this._resizeProxy();
20723
20724         if (this.centerFrame) {
20725             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20726                            Math.round(parseInt(s.height, 10)/2) );
20727         }
20728
20729         this.setDragElPos(iPageX, iPageY);
20730
20731         Roo.fly(dragEl).show();
20732     },
20733
20734     /**
20735      * The proxy is automatically resized to the dimensions of the linked
20736      * element when a drag is initiated, unless resizeFrame is set to false
20737      * @method _resizeProxy
20738      * @private
20739      */
20740     _resizeProxy: function() {
20741         if (this.resizeFrame) {
20742             var el = this.getEl();
20743             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20744         }
20745     },
20746
20747     // overrides Roo.dd.DragDrop
20748     b4MouseDown: function(e) {
20749         var x = e.getPageX();
20750         var y = e.getPageY();
20751         this.autoOffset(x, y);
20752         this.setDragElPos(x, y);
20753     },
20754
20755     // overrides Roo.dd.DragDrop
20756     b4StartDrag: function(x, y) {
20757         // show the drag frame
20758         this.showFrame(x, y);
20759     },
20760
20761     // overrides Roo.dd.DragDrop
20762     b4EndDrag: function(e) {
20763         Roo.fly(this.getDragEl()).hide();
20764     },
20765
20766     // overrides Roo.dd.DragDrop
20767     // By default we try to move the element to the last location of the frame.
20768     // This is so that the default behavior mirrors that of Roo.dd.DD.
20769     endDrag: function(e) {
20770
20771         var lel = this.getEl();
20772         var del = this.getDragEl();
20773
20774         // Show the drag frame briefly so we can get its position
20775         del.style.visibility = "";
20776
20777         this.beforeMove();
20778         // Hide the linked element before the move to get around a Safari
20779         // rendering bug.
20780         lel.style.visibility = "hidden";
20781         Roo.dd.DDM.moveToEl(lel, del);
20782         del.style.visibility = "hidden";
20783         lel.style.visibility = "";
20784
20785         this.afterDrag();
20786     },
20787
20788     beforeMove : function(){
20789
20790     },
20791
20792     afterDrag : function(){
20793
20794     },
20795
20796     toString: function() {
20797         return ("DDProxy " + this.id);
20798     }
20799
20800 });
20801 /*
20802  * Based on:
20803  * Ext JS Library 1.1.1
20804  * Copyright(c) 2006-2007, Ext JS, LLC.
20805  *
20806  * Originally Released Under LGPL - original licence link has changed is not relivant.
20807  *
20808  * Fork - LGPL
20809  * <script type="text/javascript">
20810  */
20811
20812  /**
20813  * @class Roo.dd.DDTarget
20814  * A DragDrop implementation that does not move, but can be a drop
20815  * target.  You would get the same result by simply omitting implementation
20816  * for the event callbacks, but this way we reduce the processing cost of the
20817  * event listener and the callbacks.
20818  * @extends Roo.dd.DragDrop
20819  * @constructor
20820  * @param {String} id the id of the element that is a drop target
20821  * @param {String} sGroup the group of related DragDrop objects
20822  * @param {object} config an object containing configurable attributes
20823  *                 Valid properties for DDTarget in addition to those in
20824  *                 DragDrop:
20825  *                    none
20826  */
20827 Roo.dd.DDTarget = function(id, sGroup, config) {
20828     if (id) {
20829         this.initTarget(id, sGroup, config);
20830     }
20831     if (config.listeners || config.events) { 
20832        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20833             listeners : config.listeners || {}, 
20834             events : config.events || {} 
20835         });    
20836     }
20837 };
20838
20839 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20840 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20841     toString: function() {
20842         return ("DDTarget " + this.id);
20843     }
20844 });
20845 /*
20846  * Based on:
20847  * Ext JS Library 1.1.1
20848  * Copyright(c) 2006-2007, Ext JS, LLC.
20849  *
20850  * Originally Released Under LGPL - original licence link has changed is not relivant.
20851  *
20852  * Fork - LGPL
20853  * <script type="text/javascript">
20854  */
20855  
20856
20857 /**
20858  * @class Roo.dd.ScrollManager
20859  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20860  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20861  * @singleton
20862  */
20863 Roo.dd.ScrollManager = function(){
20864     var ddm = Roo.dd.DragDropMgr;
20865     var els = {};
20866     var dragEl = null;
20867     var proc = {};
20868     
20869     
20870     
20871     var onStop = function(e){
20872         dragEl = null;
20873         clearProc();
20874     };
20875     
20876     var triggerRefresh = function(){
20877         if(ddm.dragCurrent){
20878              ddm.refreshCache(ddm.dragCurrent.groups);
20879         }
20880     };
20881     
20882     var doScroll = function(){
20883         if(ddm.dragCurrent){
20884             var dds = Roo.dd.ScrollManager;
20885             if(!dds.animate){
20886                 if(proc.el.scroll(proc.dir, dds.increment)){
20887                     triggerRefresh();
20888                 }
20889             }else{
20890                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
20891             }
20892         }
20893     };
20894     
20895     var clearProc = function(){
20896         if(proc.id){
20897             clearInterval(proc.id);
20898         }
20899         proc.id = 0;
20900         proc.el = null;
20901         proc.dir = "";
20902     };
20903     
20904     var startProc = function(el, dir){
20905          Roo.log('scroll startproc');
20906         clearProc();
20907         proc.el = el;
20908         proc.dir = dir;
20909         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
20910     };
20911     
20912     var onFire = function(e, isDrop){
20913        
20914         if(isDrop || !ddm.dragCurrent){ return; }
20915         var dds = Roo.dd.ScrollManager;
20916         if(!dragEl || dragEl != ddm.dragCurrent){
20917             dragEl = ddm.dragCurrent;
20918             // refresh regions on drag start
20919             dds.refreshCache();
20920         }
20921         
20922         var xy = Roo.lib.Event.getXY(e);
20923         var pt = new Roo.lib.Point(xy[0], xy[1]);
20924         for(var id in els){
20925             var el = els[id], r = el._region;
20926             if(r && r.contains(pt) && el.isScrollable()){
20927                 if(r.bottom - pt.y <= dds.thresh){
20928                     if(proc.el != el){
20929                         startProc(el, "down");
20930                     }
20931                     return;
20932                 }else if(r.right - pt.x <= dds.thresh){
20933                     if(proc.el != el){
20934                         startProc(el, "left");
20935                     }
20936                     return;
20937                 }else if(pt.y - r.top <= dds.thresh){
20938                     if(proc.el != el){
20939                         startProc(el, "up");
20940                     }
20941                     return;
20942                 }else if(pt.x - r.left <= dds.thresh){
20943                     if(proc.el != el){
20944                         startProc(el, "right");
20945                     }
20946                     return;
20947                 }
20948             }
20949         }
20950         clearProc();
20951     };
20952     
20953     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
20954     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
20955     
20956     return {
20957         /**
20958          * Registers new overflow element(s) to auto scroll
20959          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
20960          */
20961         register : function(el){
20962             if(el instanceof Array){
20963                 for(var i = 0, len = el.length; i < len; i++) {
20964                         this.register(el[i]);
20965                 }
20966             }else{
20967                 el = Roo.get(el);
20968                 els[el.id] = el;
20969             }
20970             Roo.dd.ScrollManager.els = els;
20971         },
20972         
20973         /**
20974          * Unregisters overflow element(s) so they are no longer scrolled
20975          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
20976          */
20977         unregister : function(el){
20978             if(el instanceof Array){
20979                 for(var i = 0, len = el.length; i < len; i++) {
20980                         this.unregister(el[i]);
20981                 }
20982             }else{
20983                 el = Roo.get(el);
20984                 delete els[el.id];
20985             }
20986         },
20987         
20988         /**
20989          * The number of pixels from the edge of a container the pointer needs to be to 
20990          * trigger scrolling (defaults to 25)
20991          * @type Number
20992          */
20993         thresh : 25,
20994         
20995         /**
20996          * The number of pixels to scroll in each scroll increment (defaults to 50)
20997          * @type Number
20998          */
20999         increment : 100,
21000         
21001         /**
21002          * The frequency of scrolls in milliseconds (defaults to 500)
21003          * @type Number
21004          */
21005         frequency : 500,
21006         
21007         /**
21008          * True to animate the scroll (defaults to true)
21009          * @type Boolean
21010          */
21011         animate: true,
21012         
21013         /**
21014          * The animation duration in seconds - 
21015          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21016          * @type Number
21017          */
21018         animDuration: .4,
21019         
21020         /**
21021          * Manually trigger a cache refresh.
21022          */
21023         refreshCache : function(){
21024             for(var id in els){
21025                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21026                     els[id]._region = els[id].getRegion();
21027                 }
21028             }
21029         }
21030     };
21031 }();/*
21032  * Based on:
21033  * Ext JS Library 1.1.1
21034  * Copyright(c) 2006-2007, Ext JS, LLC.
21035  *
21036  * Originally Released Under LGPL - original licence link has changed is not relivant.
21037  *
21038  * Fork - LGPL
21039  * <script type="text/javascript">
21040  */
21041  
21042
21043 /**
21044  * @class Roo.dd.Registry
21045  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21046  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21047  * @singleton
21048  */
21049 Roo.dd.Registry = function(){
21050     var elements = {}; 
21051     var handles = {}; 
21052     var autoIdSeed = 0;
21053
21054     var getId = function(el, autogen){
21055         if(typeof el == "string"){
21056             return el;
21057         }
21058         var id = el.id;
21059         if(!id && autogen !== false){
21060             id = "roodd-" + (++autoIdSeed);
21061             el.id = id;
21062         }
21063         return id;
21064     };
21065     
21066     return {
21067     /**
21068      * Register a drag drop element
21069      * @param {String|HTMLElement} element The id or DOM node to register
21070      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21071      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21072      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21073      * populated in the data object (if applicable):
21074      * <pre>
21075 Value      Description<br />
21076 ---------  ------------------------------------------<br />
21077 handles    Array of DOM nodes that trigger dragging<br />
21078            for the element being registered<br />
21079 isHandle   True if the element passed in triggers<br />
21080            dragging itself, else false
21081 </pre>
21082      */
21083         register : function(el, data){
21084             data = data || {};
21085             if(typeof el == "string"){
21086                 el = document.getElementById(el);
21087             }
21088             data.ddel = el;
21089             elements[getId(el)] = data;
21090             if(data.isHandle !== false){
21091                 handles[data.ddel.id] = data;
21092             }
21093             if(data.handles){
21094                 var hs = data.handles;
21095                 for(var i = 0, len = hs.length; i < len; i++){
21096                         handles[getId(hs[i])] = data;
21097                 }
21098             }
21099         },
21100
21101     /**
21102      * Unregister a drag drop element
21103      * @param {String|HTMLElement}  element The id or DOM node to unregister
21104      */
21105         unregister : function(el){
21106             var id = getId(el, false);
21107             var data = elements[id];
21108             if(data){
21109                 delete elements[id];
21110                 if(data.handles){
21111                     var hs = data.handles;
21112                     for(var i = 0, len = hs.length; i < len; i++){
21113                         delete handles[getId(hs[i], false)];
21114                     }
21115                 }
21116             }
21117         },
21118
21119     /**
21120      * Returns the handle registered for a DOM Node by id
21121      * @param {String|HTMLElement} id The DOM node or id to look up
21122      * @return {Object} handle The custom handle data
21123      */
21124         getHandle : function(id){
21125             if(typeof id != "string"){ // must be element?
21126                 id = id.id;
21127             }
21128             return handles[id];
21129         },
21130
21131     /**
21132      * Returns the handle that is registered for the DOM node that is the target of the event
21133      * @param {Event} e The event
21134      * @return {Object} handle The custom handle data
21135      */
21136         getHandleFromEvent : function(e){
21137             var t = Roo.lib.Event.getTarget(e);
21138             return t ? handles[t.id] : null;
21139         },
21140
21141     /**
21142      * Returns a custom data object that is registered for a DOM node by id
21143      * @param {String|HTMLElement} id The DOM node or id to look up
21144      * @return {Object} data The custom data
21145      */
21146         getTarget : function(id){
21147             if(typeof id != "string"){ // must be element?
21148                 id = id.id;
21149             }
21150             return elements[id];
21151         },
21152
21153     /**
21154      * Returns a custom data object that is registered for the DOM node that is the target of the event
21155      * @param {Event} e The event
21156      * @return {Object} data The custom data
21157      */
21158         getTargetFromEvent : function(e){
21159             var t = Roo.lib.Event.getTarget(e);
21160             return t ? elements[t.id] || handles[t.id] : null;
21161         }
21162     };
21163 }();/*
21164  * Based on:
21165  * Ext JS Library 1.1.1
21166  * Copyright(c) 2006-2007, Ext JS, LLC.
21167  *
21168  * Originally Released Under LGPL - original licence link has changed is not relivant.
21169  *
21170  * Fork - LGPL
21171  * <script type="text/javascript">
21172  */
21173  
21174
21175 /**
21176  * @class Roo.dd.StatusProxy
21177  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21178  * default drag proxy used by all Roo.dd components.
21179  * @constructor
21180  * @param {Object} config
21181  */
21182 Roo.dd.StatusProxy = function(config){
21183     Roo.apply(this, config);
21184     this.id = this.id || Roo.id();
21185     this.el = new Roo.Layer({
21186         dh: {
21187             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21188                 {tag: "div", cls: "x-dd-drop-icon"},
21189                 {tag: "div", cls: "x-dd-drag-ghost"}
21190             ]
21191         }, 
21192         shadow: !config || config.shadow !== false
21193     });
21194     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21195     this.dropStatus = this.dropNotAllowed;
21196 };
21197
21198 Roo.dd.StatusProxy.prototype = {
21199     /**
21200      * @cfg {String} dropAllowed
21201      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21202      */
21203     dropAllowed : "x-dd-drop-ok",
21204     /**
21205      * @cfg {String} dropNotAllowed
21206      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21207      */
21208     dropNotAllowed : "x-dd-drop-nodrop",
21209
21210     /**
21211      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21212      * over the current target element.
21213      * @param {String} cssClass The css class for the new drop status indicator image
21214      */
21215     setStatus : function(cssClass){
21216         cssClass = cssClass || this.dropNotAllowed;
21217         if(this.dropStatus != cssClass){
21218             this.el.replaceClass(this.dropStatus, cssClass);
21219             this.dropStatus = cssClass;
21220         }
21221     },
21222
21223     /**
21224      * Resets the status indicator to the default dropNotAllowed value
21225      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21226      */
21227     reset : function(clearGhost){
21228         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21229         this.dropStatus = this.dropNotAllowed;
21230         if(clearGhost){
21231             this.ghost.update("");
21232         }
21233     },
21234
21235     /**
21236      * Updates the contents of the ghost element
21237      * @param {String} html The html that will replace the current innerHTML of the ghost element
21238      */
21239     update : function(html){
21240         if(typeof html == "string"){
21241             this.ghost.update(html);
21242         }else{
21243             this.ghost.update("");
21244             html.style.margin = "0";
21245             this.ghost.dom.appendChild(html);
21246         }
21247         // ensure float = none set?? cant remember why though.
21248         var el = this.ghost.dom.firstChild;
21249                 if(el){
21250                         Roo.fly(el).setStyle('float', 'none');
21251                 }
21252     },
21253     
21254     /**
21255      * Returns the underlying proxy {@link Roo.Layer}
21256      * @return {Roo.Layer} el
21257     */
21258     getEl : function(){
21259         return this.el;
21260     },
21261
21262     /**
21263      * Returns the ghost element
21264      * @return {Roo.Element} el
21265      */
21266     getGhost : function(){
21267         return this.ghost;
21268     },
21269
21270     /**
21271      * Hides the proxy
21272      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21273      */
21274     hide : function(clear){
21275         this.el.hide();
21276         if(clear){
21277             this.reset(true);
21278         }
21279     },
21280
21281     /**
21282      * Stops the repair animation if it's currently running
21283      */
21284     stop : function(){
21285         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21286             this.anim.stop();
21287         }
21288     },
21289
21290     /**
21291      * Displays this proxy
21292      */
21293     show : function(){
21294         this.el.show();
21295     },
21296
21297     /**
21298      * Force the Layer to sync its shadow and shim positions to the element
21299      */
21300     sync : function(){
21301         this.el.sync();
21302     },
21303
21304     /**
21305      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21306      * invalid drop operation by the item being dragged.
21307      * @param {Array} xy The XY position of the element ([x, y])
21308      * @param {Function} callback The function to call after the repair is complete
21309      * @param {Object} scope The scope in which to execute the callback
21310      */
21311     repair : function(xy, callback, scope){
21312         this.callback = callback;
21313         this.scope = scope;
21314         if(xy && this.animRepair !== false){
21315             this.el.addClass("x-dd-drag-repair");
21316             this.el.hideUnders(true);
21317             this.anim = this.el.shift({
21318                 duration: this.repairDuration || .5,
21319                 easing: 'easeOut',
21320                 xy: xy,
21321                 stopFx: true,
21322                 callback: this.afterRepair,
21323                 scope: this
21324             });
21325         }else{
21326             this.afterRepair();
21327         }
21328     },
21329
21330     // private
21331     afterRepair : function(){
21332         this.hide(true);
21333         if(typeof this.callback == "function"){
21334             this.callback.call(this.scope || this);
21335         }
21336         this.callback = null;
21337         this.scope = null;
21338     }
21339 };/*
21340  * Based on:
21341  * Ext JS Library 1.1.1
21342  * Copyright(c) 2006-2007, Ext JS, LLC.
21343  *
21344  * Originally Released Under LGPL - original licence link has changed is not relivant.
21345  *
21346  * Fork - LGPL
21347  * <script type="text/javascript">
21348  */
21349
21350 /**
21351  * @class Roo.dd.DragSource
21352  * @extends Roo.dd.DDProxy
21353  * A simple class that provides the basic implementation needed to make any element draggable.
21354  * @constructor
21355  * @param {String/HTMLElement/Element} el The container element
21356  * @param {Object} config
21357  */
21358 Roo.dd.DragSource = function(el, config){
21359     this.el = Roo.get(el);
21360     this.dragData = {};
21361     
21362     Roo.apply(this, config);
21363     
21364     if(!this.proxy){
21365         this.proxy = new Roo.dd.StatusProxy();
21366     }
21367
21368     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21369           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21370     
21371     this.dragging = false;
21372 };
21373
21374 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21375     /**
21376      * @cfg {String} dropAllowed
21377      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21378      */
21379     dropAllowed : "x-dd-drop-ok",
21380     /**
21381      * @cfg {String} dropNotAllowed
21382      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21383      */
21384     dropNotAllowed : "x-dd-drop-nodrop",
21385
21386     /**
21387      * Returns the data object associated with this drag source
21388      * @return {Object} data An object containing arbitrary data
21389      */
21390     getDragData : function(e){
21391         return this.dragData;
21392     },
21393
21394     // private
21395     onDragEnter : function(e, id){
21396         var target = Roo.dd.DragDropMgr.getDDById(id);
21397         this.cachedTarget = target;
21398         if(this.beforeDragEnter(target, e, id) !== false){
21399             if(target.isNotifyTarget){
21400                 var status = target.notifyEnter(this, e, this.dragData);
21401                 this.proxy.setStatus(status);
21402             }else{
21403                 this.proxy.setStatus(this.dropAllowed);
21404             }
21405             
21406             if(this.afterDragEnter){
21407                 /**
21408                  * An empty function by default, but provided so that you can perform a custom action
21409                  * when the dragged item enters the drop target by providing an implementation.
21410                  * @param {Roo.dd.DragDrop} target The drop target
21411                  * @param {Event} e The event object
21412                  * @param {String} id The id of the dragged element
21413                  * @method afterDragEnter
21414                  */
21415                 this.afterDragEnter(target, e, id);
21416             }
21417         }
21418     },
21419
21420     /**
21421      * An empty function by default, but provided so that you can perform a custom action
21422      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21423      * @param {Roo.dd.DragDrop} target The drop target
21424      * @param {Event} e The event object
21425      * @param {String} id The id of the dragged element
21426      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21427      */
21428     beforeDragEnter : function(target, e, id){
21429         return true;
21430     },
21431
21432     // private
21433     alignElWithMouse: function() {
21434         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21435         this.proxy.sync();
21436     },
21437
21438     // private
21439     onDragOver : function(e, id){
21440         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21441         if(this.beforeDragOver(target, e, id) !== false){
21442             if(target.isNotifyTarget){
21443                 var status = target.notifyOver(this, e, this.dragData);
21444                 this.proxy.setStatus(status);
21445             }
21446
21447             if(this.afterDragOver){
21448                 /**
21449                  * An empty function by default, but provided so that you can perform a custom action
21450                  * while the dragged item is over the drop target by providing an implementation.
21451                  * @param {Roo.dd.DragDrop} target The drop target
21452                  * @param {Event} e The event object
21453                  * @param {String} id The id of the dragged element
21454                  * @method afterDragOver
21455                  */
21456                 this.afterDragOver(target, e, id);
21457             }
21458         }
21459     },
21460
21461     /**
21462      * An empty function by default, but provided so that you can perform a custom action
21463      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21464      * @param {Roo.dd.DragDrop} target The drop target
21465      * @param {Event} e The event object
21466      * @param {String} id The id of the dragged element
21467      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21468      */
21469     beforeDragOver : function(target, e, id){
21470         return true;
21471     },
21472
21473     // private
21474     onDragOut : function(e, id){
21475         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21476         if(this.beforeDragOut(target, e, id) !== false){
21477             if(target.isNotifyTarget){
21478                 target.notifyOut(this, e, this.dragData);
21479             }
21480             this.proxy.reset();
21481             if(this.afterDragOut){
21482                 /**
21483                  * An empty function by default, but provided so that you can perform a custom action
21484                  * after the dragged item is dragged out of the target without dropping.
21485                  * @param {Roo.dd.DragDrop} target The drop target
21486                  * @param {Event} e The event object
21487                  * @param {String} id The id of the dragged element
21488                  * @method afterDragOut
21489                  */
21490                 this.afterDragOut(target, e, id);
21491             }
21492         }
21493         this.cachedTarget = null;
21494     },
21495
21496     /**
21497      * An empty function by default, but provided so that you can perform a custom action before the dragged
21498      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21499      * @param {Roo.dd.DragDrop} target The drop target
21500      * @param {Event} e The event object
21501      * @param {String} id The id of the dragged element
21502      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21503      */
21504     beforeDragOut : function(target, e, id){
21505         return true;
21506     },
21507     
21508     // private
21509     onDragDrop : function(e, id){
21510         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21511         if(this.beforeDragDrop(target, e, id) !== false){
21512             if(target.isNotifyTarget){
21513                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21514                     this.onValidDrop(target, e, id);
21515                 }else{
21516                     this.onInvalidDrop(target, e, id);
21517                 }
21518             }else{
21519                 this.onValidDrop(target, e, id);
21520             }
21521             
21522             if(this.afterDragDrop){
21523                 /**
21524                  * An empty function by default, but provided so that you can perform a custom action
21525                  * after a valid drag drop has occurred by providing an implementation.
21526                  * @param {Roo.dd.DragDrop} target The drop target
21527                  * @param {Event} e The event object
21528                  * @param {String} id The id of the dropped element
21529                  * @method afterDragDrop
21530                  */
21531                 this.afterDragDrop(target, e, id);
21532             }
21533         }
21534         delete this.cachedTarget;
21535     },
21536
21537     /**
21538      * An empty function by default, but provided so that you can perform a custom action before the dragged
21539      * item is dropped onto the target and optionally cancel the onDragDrop.
21540      * @param {Roo.dd.DragDrop} target The drop target
21541      * @param {Event} e The event object
21542      * @param {String} id The id of the dragged element
21543      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21544      */
21545     beforeDragDrop : function(target, e, id){
21546         return true;
21547     },
21548
21549     // private
21550     onValidDrop : function(target, e, id){
21551         this.hideProxy();
21552         if(this.afterValidDrop){
21553             /**
21554              * An empty function by default, but provided so that you can perform a custom action
21555              * after a valid drop has occurred by providing an implementation.
21556              * @param {Object} target The target DD 
21557              * @param {Event} e The event object
21558              * @param {String} id The id of the dropped element
21559              * @method afterInvalidDrop
21560              */
21561             this.afterValidDrop(target, e, id);
21562         }
21563     },
21564
21565     // private
21566     getRepairXY : function(e, data){
21567         return this.el.getXY();  
21568     },
21569
21570     // private
21571     onInvalidDrop : function(target, e, id){
21572         this.beforeInvalidDrop(target, e, id);
21573         if(this.cachedTarget){
21574             if(this.cachedTarget.isNotifyTarget){
21575                 this.cachedTarget.notifyOut(this, e, this.dragData);
21576             }
21577             this.cacheTarget = null;
21578         }
21579         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21580
21581         if(this.afterInvalidDrop){
21582             /**
21583              * An empty function by default, but provided so that you can perform a custom action
21584              * after an invalid drop has occurred by providing an implementation.
21585              * @param {Event} e The event object
21586              * @param {String} id The id of the dropped element
21587              * @method afterInvalidDrop
21588              */
21589             this.afterInvalidDrop(e, id);
21590         }
21591     },
21592
21593     // private
21594     afterRepair : function(){
21595         if(Roo.enableFx){
21596             this.el.highlight(this.hlColor || "c3daf9");
21597         }
21598         this.dragging = false;
21599     },
21600
21601     /**
21602      * An empty function by default, but provided so that you can perform a custom action after an invalid
21603      * drop has occurred.
21604      * @param {Roo.dd.DragDrop} target The drop target
21605      * @param {Event} e The event object
21606      * @param {String} id The id of the dragged element
21607      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21608      */
21609     beforeInvalidDrop : function(target, e, id){
21610         return true;
21611     },
21612
21613     // private
21614     handleMouseDown : function(e){
21615         if(this.dragging) {
21616             return;
21617         }
21618         var data = this.getDragData(e);
21619         if(data && this.onBeforeDrag(data, e) !== false){
21620             this.dragData = data;
21621             this.proxy.stop();
21622             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21623         } 
21624     },
21625
21626     /**
21627      * An empty function by default, but provided so that you can perform a custom action before the initial
21628      * drag event begins and optionally cancel it.
21629      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21630      * @param {Event} e The event object
21631      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21632      */
21633     onBeforeDrag : function(data, e){
21634         return true;
21635     },
21636
21637     /**
21638      * An empty function by default, but provided so that you can perform a custom action once the initial
21639      * drag event has begun.  The drag cannot be canceled from this function.
21640      * @param {Number} x The x position of the click on the dragged object
21641      * @param {Number} y The y position of the click on the dragged object
21642      */
21643     onStartDrag : Roo.emptyFn,
21644
21645     // private - YUI override
21646     startDrag : function(x, y){
21647         this.proxy.reset();
21648         this.dragging = true;
21649         this.proxy.update("");
21650         this.onInitDrag(x, y);
21651         this.proxy.show();
21652     },
21653
21654     // private
21655     onInitDrag : function(x, y){
21656         var clone = this.el.dom.cloneNode(true);
21657         clone.id = Roo.id(); // prevent duplicate ids
21658         this.proxy.update(clone);
21659         this.onStartDrag(x, y);
21660         return true;
21661     },
21662
21663     /**
21664      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21665      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21666      */
21667     getProxy : function(){
21668         return this.proxy;  
21669     },
21670
21671     /**
21672      * Hides the drag source's {@link Roo.dd.StatusProxy}
21673      */
21674     hideProxy : function(){
21675         this.proxy.hide();  
21676         this.proxy.reset(true);
21677         this.dragging = false;
21678     },
21679
21680     // private
21681     triggerCacheRefresh : function(){
21682         Roo.dd.DDM.refreshCache(this.groups);
21683     },
21684
21685     // private - override to prevent hiding
21686     b4EndDrag: function(e) {
21687     },
21688
21689     // private - override to prevent moving
21690     endDrag : function(e){
21691         this.onEndDrag(this.dragData, e);
21692     },
21693
21694     // private
21695     onEndDrag : function(data, e){
21696     },
21697     
21698     // private - pin to cursor
21699     autoOffset : function(x, y) {
21700         this.setDelta(-12, -20);
21701     }    
21702 });/*
21703  * Based on:
21704  * Ext JS Library 1.1.1
21705  * Copyright(c) 2006-2007, Ext JS, LLC.
21706  *
21707  * Originally Released Under LGPL - original licence link has changed is not relivant.
21708  *
21709  * Fork - LGPL
21710  * <script type="text/javascript">
21711  */
21712
21713
21714 /**
21715  * @class Roo.dd.DropTarget
21716  * @extends Roo.dd.DDTarget
21717  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21718  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21719  * @constructor
21720  * @param {String/HTMLElement/Element} el The container element
21721  * @param {Object} config
21722  */
21723 Roo.dd.DropTarget = function(el, config){
21724     this.el = Roo.get(el);
21725     
21726     var listeners = false; ;
21727     if (config && config.listeners) {
21728         listeners= config.listeners;
21729         delete config.listeners;
21730     }
21731     Roo.apply(this, config);
21732     
21733     if(this.containerScroll){
21734         Roo.dd.ScrollManager.register(this.el);
21735     }
21736     this.addEvents( {
21737          /**
21738          * @scope Roo.dd.DropTarget
21739          */
21740          
21741          /**
21742          * @event enter
21743          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21744          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21745          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21746          * 
21747          * IMPORTANT : it should set this.overClass and this.dropAllowed
21748          * 
21749          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21750          * @param {Event} e The event
21751          * @param {Object} data An object containing arbitrary data supplied by the drag source
21752          */
21753         "enter" : true,
21754         
21755          /**
21756          * @event over
21757          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21758          * This method will be called on every mouse movement while the drag source is over the drop target.
21759          * This default implementation simply returns the dropAllowed config value.
21760          * 
21761          * IMPORTANT : it should set this.dropAllowed
21762          * 
21763          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21764          * @param {Event} e The event
21765          * @param {Object} data An object containing arbitrary data supplied by the drag source
21766          
21767          */
21768         "over" : true,
21769         /**
21770          * @event out
21771          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21772          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21773          * overClass (if any) from the drop element.
21774          * 
21775          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21776          * @param {Event} e The event
21777          * @param {Object} data An object containing arbitrary data supplied by the drag source
21778          */
21779          "out" : true,
21780          
21781         /**
21782          * @event drop
21783          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21784          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21785          * implementation that does something to process the drop event and returns true so that the drag source's
21786          * repair action does not run.
21787          * 
21788          * IMPORTANT : it should set this.success
21789          * 
21790          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21791          * @param {Event} e The event
21792          * @param {Object} data An object containing arbitrary data supplied by the drag source
21793         */
21794          "drop" : true
21795     });
21796             
21797      
21798     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21799         this.el.dom, 
21800         this.ddGroup || this.group,
21801         {
21802             isTarget: true,
21803             listeners : listeners || {} 
21804            
21805         
21806         }
21807     );
21808
21809 };
21810
21811 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21812     /**
21813      * @cfg {String} overClass
21814      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21815      */
21816      /**
21817      * @cfg {String} ddGroup
21818      * The drag drop group to handle drop events for
21819      */
21820      
21821     /**
21822      * @cfg {String} dropAllowed
21823      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21824      */
21825     dropAllowed : "x-dd-drop-ok",
21826     /**
21827      * @cfg {String} dropNotAllowed
21828      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21829      */
21830     dropNotAllowed : "x-dd-drop-nodrop",
21831     /**
21832      * @cfg {boolean} success
21833      * set this after drop listener.. 
21834      */
21835     success : false,
21836     /**
21837      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21838      * if the drop point is valid for over/enter..
21839      */
21840     valid : false,
21841     // private
21842     isTarget : true,
21843
21844     // private
21845     isNotifyTarget : true,
21846     
21847     /**
21848      * @hide
21849      */
21850     notifyEnter : function(dd, e, data)
21851     {
21852         this.valid = true;
21853         this.fireEvent('enter', dd, e, data);
21854         if(this.overClass){
21855             this.el.addClass(this.overClass);
21856         }
21857         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21858             this.valid ? this.dropAllowed : this.dropNotAllowed
21859         );
21860     },
21861
21862     /**
21863      * @hide
21864      */
21865     notifyOver : function(dd, e, data)
21866     {
21867         this.valid = true;
21868         this.fireEvent('over', dd, e, data);
21869         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21870             this.valid ? this.dropAllowed : this.dropNotAllowed
21871         );
21872     },
21873
21874     /**
21875      * @hide
21876      */
21877     notifyOut : function(dd, e, data)
21878     {
21879         this.fireEvent('out', dd, e, data);
21880         if(this.overClass){
21881             this.el.removeClass(this.overClass);
21882         }
21883     },
21884
21885     /**
21886      * @hide
21887      */
21888     notifyDrop : function(dd, e, data)
21889     {
21890         this.success = false;
21891         this.fireEvent('drop', dd, e, data);
21892         return this.success;
21893     }
21894 });/*
21895  * Based on:
21896  * Ext JS Library 1.1.1
21897  * Copyright(c) 2006-2007, Ext JS, LLC.
21898  *
21899  * Originally Released Under LGPL - original licence link has changed is not relivant.
21900  *
21901  * Fork - LGPL
21902  * <script type="text/javascript">
21903  */
21904
21905
21906 /**
21907  * @class Roo.dd.DragZone
21908  * @extends Roo.dd.DragSource
21909  * This class provides a container DD instance that proxies for multiple child node sources.<br />
21910  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
21911  * @constructor
21912  * @param {String/HTMLElement/Element} el The container element
21913  * @param {Object} config
21914  */
21915 Roo.dd.DragZone = function(el, config){
21916     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
21917     if(this.containerScroll){
21918         Roo.dd.ScrollManager.register(this.el);
21919     }
21920 };
21921
21922 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
21923     /**
21924      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
21925      * for auto scrolling during drag operations.
21926      */
21927     /**
21928      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
21929      * method after a failed drop (defaults to "c3daf9" - light blue)
21930      */
21931
21932     /**
21933      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
21934      * for a valid target to drag based on the mouse down. Override this method
21935      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
21936      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
21937      * @param {EventObject} e The mouse down event
21938      * @return {Object} The dragData
21939      */
21940     getDragData : function(e){
21941         return Roo.dd.Registry.getHandleFromEvent(e);
21942     },
21943     
21944     /**
21945      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
21946      * this.dragData.ddel
21947      * @param {Number} x The x position of the click on the dragged object
21948      * @param {Number} y The y position of the click on the dragged object
21949      * @return {Boolean} true to continue the drag, false to cancel
21950      */
21951     onInitDrag : function(x, y){
21952         this.proxy.update(this.dragData.ddel.cloneNode(true));
21953         this.onStartDrag(x, y);
21954         return true;
21955     },
21956     
21957     /**
21958      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
21959      */
21960     afterRepair : function(){
21961         if(Roo.enableFx){
21962             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
21963         }
21964         this.dragging = false;
21965     },
21966
21967     /**
21968      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
21969      * the XY of this.dragData.ddel
21970      * @param {EventObject} e The mouse up event
21971      * @return {Array} The xy location (e.g. [100, 200])
21972      */
21973     getRepairXY : function(e){
21974         return Roo.Element.fly(this.dragData.ddel).getXY();  
21975     }
21976 });/*
21977  * Based on:
21978  * Ext JS Library 1.1.1
21979  * Copyright(c) 2006-2007, Ext JS, LLC.
21980  *
21981  * Originally Released Under LGPL - original licence link has changed is not relivant.
21982  *
21983  * Fork - LGPL
21984  * <script type="text/javascript">
21985  */
21986 /**
21987  * @class Roo.dd.DropZone
21988  * @extends Roo.dd.DropTarget
21989  * This class provides a container DD instance that proxies for multiple child node targets.<br />
21990  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
21991  * @constructor
21992  * @param {String/HTMLElement/Element} el The container element
21993  * @param {Object} config
21994  */
21995 Roo.dd.DropZone = function(el, config){
21996     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
21997 };
21998
21999 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22000     /**
22001      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22002      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22003      * provide your own custom lookup.
22004      * @param {Event} e The event
22005      * @return {Object} data The custom data
22006      */
22007     getTargetFromEvent : function(e){
22008         return Roo.dd.Registry.getTargetFromEvent(e);
22009     },
22010
22011     /**
22012      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22013      * that it has registered.  This method has no default implementation and should be overridden to provide
22014      * node-specific processing if necessary.
22015      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22016      * {@link #getTargetFromEvent} for this node)
22017      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22018      * @param {Event} e The event
22019      * @param {Object} data An object containing arbitrary data supplied by the drag source
22020      */
22021     onNodeEnter : function(n, dd, e, data){
22022         
22023     },
22024
22025     /**
22026      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22027      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22028      * overridden to provide the proper feedback.
22029      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22030      * {@link #getTargetFromEvent} for this node)
22031      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22032      * @param {Event} e The event
22033      * @param {Object} data An object containing arbitrary data supplied by the drag source
22034      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22035      * underlying {@link Roo.dd.StatusProxy} can be updated
22036      */
22037     onNodeOver : function(n, dd, e, data){
22038         return this.dropAllowed;
22039     },
22040
22041     /**
22042      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22043      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22044      * node-specific processing if necessary.
22045      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22046      * {@link #getTargetFromEvent} for this node)
22047      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22048      * @param {Event} e The event
22049      * @param {Object} data An object containing arbitrary data supplied by the drag source
22050      */
22051     onNodeOut : function(n, dd, e, data){
22052         
22053     },
22054
22055     /**
22056      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22057      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22058      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22059      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22060      * {@link #getTargetFromEvent} for this node)
22061      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22062      * @param {Event} e The event
22063      * @param {Object} data An object containing arbitrary data supplied by the drag source
22064      * @return {Boolean} True if the drop was valid, else false
22065      */
22066     onNodeDrop : function(n, dd, e, data){
22067         return false;
22068     },
22069
22070     /**
22071      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22072      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22073      * it should be overridden to provide the proper feedback if necessary.
22074      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22075      * @param {Event} e The event
22076      * @param {Object} data An object containing arbitrary data supplied by the drag source
22077      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22078      * underlying {@link Roo.dd.StatusProxy} can be updated
22079      */
22080     onContainerOver : function(dd, e, data){
22081         return this.dropNotAllowed;
22082     },
22083
22084     /**
22085      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22086      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22087      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22088      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22089      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22090      * @param {Event} e The event
22091      * @param {Object} data An object containing arbitrary data supplied by the drag source
22092      * @return {Boolean} True if the drop was valid, else false
22093      */
22094     onContainerDrop : function(dd, e, data){
22095         return false;
22096     },
22097
22098     /**
22099      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22100      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22101      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22102      * you should override this method and provide a custom implementation.
22103      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22104      * @param {Event} e The event
22105      * @param {Object} data An object containing arbitrary data supplied by the drag source
22106      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22107      * underlying {@link Roo.dd.StatusProxy} can be updated
22108      */
22109     notifyEnter : function(dd, e, data){
22110         return this.dropNotAllowed;
22111     },
22112
22113     /**
22114      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22115      * This method will be called on every mouse movement while the drag source is over the drop zone.
22116      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22117      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22118      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22119      * registered node, it will call {@link #onContainerOver}.
22120      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22121      * @param {Event} e The event
22122      * @param {Object} data An object containing arbitrary data supplied by the drag source
22123      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22124      * underlying {@link Roo.dd.StatusProxy} can be updated
22125      */
22126     notifyOver : function(dd, e, data){
22127         var n = this.getTargetFromEvent(e);
22128         if(!n){ // not over valid drop target
22129             if(this.lastOverNode){
22130                 this.onNodeOut(this.lastOverNode, dd, e, data);
22131                 this.lastOverNode = null;
22132             }
22133             return this.onContainerOver(dd, e, data);
22134         }
22135         if(this.lastOverNode != n){
22136             if(this.lastOverNode){
22137                 this.onNodeOut(this.lastOverNode, dd, e, data);
22138             }
22139             this.onNodeEnter(n, dd, e, data);
22140             this.lastOverNode = n;
22141         }
22142         return this.onNodeOver(n, dd, e, data);
22143     },
22144
22145     /**
22146      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22147      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22148      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22149      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22150      * @param {Event} e The event
22151      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22152      */
22153     notifyOut : function(dd, e, data){
22154         if(this.lastOverNode){
22155             this.onNodeOut(this.lastOverNode, dd, e, data);
22156             this.lastOverNode = null;
22157         }
22158     },
22159
22160     /**
22161      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22162      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22163      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22164      * otherwise it will call {@link #onContainerDrop}.
22165      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22166      * @param {Event} e The event
22167      * @param {Object} data An object containing arbitrary data supplied by the drag source
22168      * @return {Boolean} True if the drop was valid, else false
22169      */
22170     notifyDrop : function(dd, e, data){
22171         if(this.lastOverNode){
22172             this.onNodeOut(this.lastOverNode, dd, e, data);
22173             this.lastOverNode = null;
22174         }
22175         var n = this.getTargetFromEvent(e);
22176         return n ?
22177             this.onNodeDrop(n, dd, e, data) :
22178             this.onContainerDrop(dd, e, data);
22179     },
22180
22181     // private
22182     triggerCacheRefresh : function(){
22183         Roo.dd.DDM.refreshCache(this.groups);
22184     }  
22185 });/*
22186  * Based on:
22187  * Ext JS Library 1.1.1
22188  * Copyright(c) 2006-2007, Ext JS, LLC.
22189  *
22190  * Originally Released Under LGPL - original licence link has changed is not relivant.
22191  *
22192  * Fork - LGPL
22193  * <script type="text/javascript">
22194  */
22195
22196
22197 /**
22198  * @class Roo.data.SortTypes
22199  * @singleton
22200  * Defines the default sorting (casting?) comparison functions used when sorting data.
22201  */
22202 Roo.data.SortTypes = {
22203     /**
22204      * Default sort that does nothing
22205      * @param {Mixed} s The value being converted
22206      * @return {Mixed} The comparison value
22207      */
22208     none : function(s){
22209         return s;
22210     },
22211     
22212     /**
22213      * The regular expression used to strip tags
22214      * @type {RegExp}
22215      * @property
22216      */
22217     stripTagsRE : /<\/?[^>]+>/gi,
22218     
22219     /**
22220      * Strips all HTML tags to sort on text only
22221      * @param {Mixed} s The value being converted
22222      * @return {String} The comparison value
22223      */
22224     asText : function(s){
22225         return String(s).replace(this.stripTagsRE, "");
22226     },
22227     
22228     /**
22229      * Strips all HTML tags to sort on text only - Case insensitive
22230      * @param {Mixed} s The value being converted
22231      * @return {String} The comparison value
22232      */
22233     asUCText : function(s){
22234         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22235     },
22236     
22237     /**
22238      * Case insensitive string
22239      * @param {Mixed} s The value being converted
22240      * @return {String} The comparison value
22241      */
22242     asUCString : function(s) {
22243         return String(s).toUpperCase();
22244     },
22245     
22246     /**
22247      * Date sorting
22248      * @param {Mixed} s The value being converted
22249      * @return {Number} The comparison value
22250      */
22251     asDate : function(s) {
22252         if(!s){
22253             return 0;
22254         }
22255         if(s instanceof Date){
22256             return s.getTime();
22257         }
22258         return Date.parse(String(s));
22259     },
22260     
22261     /**
22262      * Float sorting
22263      * @param {Mixed} s The value being converted
22264      * @return {Float} The comparison value
22265      */
22266     asFloat : function(s) {
22267         var val = parseFloat(String(s).replace(/,/g, ""));
22268         if(isNaN(val)) {
22269             val = 0;
22270         }
22271         return val;
22272     },
22273     
22274     /**
22275      * Integer sorting
22276      * @param {Mixed} s The value being converted
22277      * @return {Number} The comparison value
22278      */
22279     asInt : function(s) {
22280         var val = parseInt(String(s).replace(/,/g, ""));
22281         if(isNaN(val)) {
22282             val = 0;
22283         }
22284         return val;
22285     }
22286 };/*
22287  * Based on:
22288  * Ext JS Library 1.1.1
22289  * Copyright(c) 2006-2007, Ext JS, LLC.
22290  *
22291  * Originally Released Under LGPL - original licence link has changed is not relivant.
22292  *
22293  * Fork - LGPL
22294  * <script type="text/javascript">
22295  */
22296
22297 /**
22298 * @class Roo.data.Record
22299  * Instances of this class encapsulate both record <em>definition</em> information, and record
22300  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22301  * to access Records cached in an {@link Roo.data.Store} object.<br>
22302  * <p>
22303  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22304  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22305  * objects.<br>
22306  * <p>
22307  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22308  * @constructor
22309  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22310  * {@link #create}. The parameters are the same.
22311  * @param {Array} data An associative Array of data values keyed by the field name.
22312  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22313  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22314  * not specified an integer id is generated.
22315  */
22316 Roo.data.Record = function(data, id){
22317     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22318     this.data = data;
22319 };
22320
22321 /**
22322  * Generate a constructor for a specific record layout.
22323  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22324  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22325  * Each field definition object may contain the following properties: <ul>
22326  * <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,
22327  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22328  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22329  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22330  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22331  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22332  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22333  * this may be omitted.</p></li>
22334  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22335  * <ul><li>auto (Default, implies no conversion)</li>
22336  * <li>string</li>
22337  * <li>int</li>
22338  * <li>float</li>
22339  * <li>boolean</li>
22340  * <li>date</li></ul></p></li>
22341  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22342  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22343  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22344  * by the Reader into an object that will be stored in the Record. It is passed the
22345  * following parameters:<ul>
22346  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22347  * </ul></p></li>
22348  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22349  * </ul>
22350  * <br>usage:<br><pre><code>
22351 var TopicRecord = Roo.data.Record.create(
22352     {name: 'title', mapping: 'topic_title'},
22353     {name: 'author', mapping: 'username'},
22354     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22355     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22356     {name: 'lastPoster', mapping: 'user2'},
22357     {name: 'excerpt', mapping: 'post_text'}
22358 );
22359
22360 var myNewRecord = new TopicRecord({
22361     title: 'Do my job please',
22362     author: 'noobie',
22363     totalPosts: 1,
22364     lastPost: new Date(),
22365     lastPoster: 'Animal',
22366     excerpt: 'No way dude!'
22367 });
22368 myStore.add(myNewRecord);
22369 </code></pre>
22370  * @method create
22371  * @static
22372  */
22373 Roo.data.Record.create = function(o){
22374     var f = function(){
22375         f.superclass.constructor.apply(this, arguments);
22376     };
22377     Roo.extend(f, Roo.data.Record);
22378     var p = f.prototype;
22379     p.fields = new Roo.util.MixedCollection(false, function(field){
22380         return field.name;
22381     });
22382     for(var i = 0, len = o.length; i < len; i++){
22383         p.fields.add(new Roo.data.Field(o[i]));
22384     }
22385     f.getField = function(name){
22386         return p.fields.get(name);  
22387     };
22388     return f;
22389 };
22390
22391 Roo.data.Record.AUTO_ID = 1000;
22392 Roo.data.Record.EDIT = 'edit';
22393 Roo.data.Record.REJECT = 'reject';
22394 Roo.data.Record.COMMIT = 'commit';
22395
22396 Roo.data.Record.prototype = {
22397     /**
22398      * Readonly flag - true if this record has been modified.
22399      * @type Boolean
22400      */
22401     dirty : false,
22402     editing : false,
22403     error: null,
22404     modified: null,
22405
22406     // private
22407     join : function(store){
22408         this.store = store;
22409     },
22410
22411     /**
22412      * Set the named field to the specified value.
22413      * @param {String} name The name of the field to set.
22414      * @param {Object} value The value to set the field to.
22415      */
22416     set : function(name, value){
22417         if(this.data[name] == value){
22418             return;
22419         }
22420         this.dirty = true;
22421         if(!this.modified){
22422             this.modified = {};
22423         }
22424         if(typeof this.modified[name] == 'undefined'){
22425             this.modified[name] = this.data[name];
22426         }
22427         this.data[name] = value;
22428         if(!this.editing && this.store){
22429             this.store.afterEdit(this);
22430         }       
22431     },
22432
22433     /**
22434      * Get the value of the named field.
22435      * @param {String} name The name of the field to get the value of.
22436      * @return {Object} The value of the field.
22437      */
22438     get : function(name){
22439         return this.data[name]; 
22440     },
22441
22442     // private
22443     beginEdit : function(){
22444         this.editing = true;
22445         this.modified = {}; 
22446     },
22447
22448     // private
22449     cancelEdit : function(){
22450         this.editing = false;
22451         delete this.modified;
22452     },
22453
22454     // private
22455     endEdit : function(){
22456         this.editing = false;
22457         if(this.dirty && this.store){
22458             this.store.afterEdit(this);
22459         }
22460     },
22461
22462     /**
22463      * Usually called by the {@link Roo.data.Store} which owns the Record.
22464      * Rejects all changes made to the Record since either creation, or the last commit operation.
22465      * Modified fields are reverted to their original values.
22466      * <p>
22467      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22468      * of reject operations.
22469      */
22470     reject : function(){
22471         var m = this.modified;
22472         for(var n in m){
22473             if(typeof m[n] != "function"){
22474                 this.data[n] = m[n];
22475             }
22476         }
22477         this.dirty = false;
22478         delete this.modified;
22479         this.editing = false;
22480         if(this.store){
22481             this.store.afterReject(this);
22482         }
22483     },
22484
22485     /**
22486      * Usually called by the {@link Roo.data.Store} which owns the Record.
22487      * Commits all changes made to the Record since either creation, or the last commit operation.
22488      * <p>
22489      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22490      * of commit operations.
22491      */
22492     commit : function(){
22493         this.dirty = false;
22494         delete this.modified;
22495         this.editing = false;
22496         if(this.store){
22497             this.store.afterCommit(this);
22498         }
22499     },
22500
22501     // private
22502     hasError : function(){
22503         return this.error != null;
22504     },
22505
22506     // private
22507     clearError : function(){
22508         this.error = null;
22509     },
22510
22511     /**
22512      * Creates a copy of this record.
22513      * @param {String} id (optional) A new record id if you don't want to use this record's id
22514      * @return {Record}
22515      */
22516     copy : function(newId) {
22517         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22518     }
22519 };/*
22520  * Based on:
22521  * Ext JS Library 1.1.1
22522  * Copyright(c) 2006-2007, Ext JS, LLC.
22523  *
22524  * Originally Released Under LGPL - original licence link has changed is not relivant.
22525  *
22526  * Fork - LGPL
22527  * <script type="text/javascript">
22528  */
22529
22530
22531
22532 /**
22533  * @class Roo.data.Store
22534  * @extends Roo.util.Observable
22535  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22536  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22537  * <p>
22538  * 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
22539  * has no knowledge of the format of the data returned by the Proxy.<br>
22540  * <p>
22541  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22542  * instances from the data object. These records are cached and made available through accessor functions.
22543  * @constructor
22544  * Creates a new Store.
22545  * @param {Object} config A config object containing the objects needed for the Store to access data,
22546  * and read the data into Records.
22547  */
22548 Roo.data.Store = function(config){
22549     this.data = new Roo.util.MixedCollection(false);
22550     this.data.getKey = function(o){
22551         return o.id;
22552     };
22553     this.baseParams = {};
22554     // private
22555     this.paramNames = {
22556         "start" : "start",
22557         "limit" : "limit",
22558         "sort" : "sort",
22559         "dir" : "dir",
22560         "multisort" : "_multisort"
22561     };
22562
22563     if(config && config.data){
22564         this.inlineData = config.data;
22565         delete config.data;
22566     }
22567
22568     Roo.apply(this, config);
22569     
22570     if(this.reader){ // reader passed
22571         this.reader = Roo.factory(this.reader, Roo.data);
22572         this.reader.xmodule = this.xmodule || false;
22573         if(!this.recordType){
22574             this.recordType = this.reader.recordType;
22575         }
22576         if(this.reader.onMetaChange){
22577             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22578         }
22579     }
22580
22581     if(this.recordType){
22582         this.fields = this.recordType.prototype.fields;
22583     }
22584     this.modified = [];
22585
22586     this.addEvents({
22587         /**
22588          * @event datachanged
22589          * Fires when the data cache has changed, and a widget which is using this Store
22590          * as a Record cache should refresh its view.
22591          * @param {Store} this
22592          */
22593         datachanged : true,
22594         /**
22595          * @event metachange
22596          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22597          * @param {Store} this
22598          * @param {Object} meta The JSON metadata
22599          */
22600         metachange : true,
22601         /**
22602          * @event add
22603          * Fires when Records have been added to the Store
22604          * @param {Store} this
22605          * @param {Roo.data.Record[]} records The array of Records added
22606          * @param {Number} index The index at which the record(s) were added
22607          */
22608         add : true,
22609         /**
22610          * @event remove
22611          * Fires when a Record has been removed from the Store
22612          * @param {Store} this
22613          * @param {Roo.data.Record} record The Record that was removed
22614          * @param {Number} index The index at which the record was removed
22615          */
22616         remove : true,
22617         /**
22618          * @event update
22619          * Fires when a Record has been updated
22620          * @param {Store} this
22621          * @param {Roo.data.Record} record The Record that was updated
22622          * @param {String} operation The update operation being performed.  Value may be one of:
22623          * <pre><code>
22624  Roo.data.Record.EDIT
22625  Roo.data.Record.REJECT
22626  Roo.data.Record.COMMIT
22627          * </code></pre>
22628          */
22629         update : true,
22630         /**
22631          * @event clear
22632          * Fires when the data cache has been cleared.
22633          * @param {Store} this
22634          */
22635         clear : true,
22636         /**
22637          * @event beforeload
22638          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22639          * the load action will be canceled.
22640          * @param {Store} this
22641          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22642          */
22643         beforeload : true,
22644         /**
22645          * @event beforeloadadd
22646          * Fires after a new set of Records has been loaded.
22647          * @param {Store} this
22648          * @param {Roo.data.Record[]} records The Records that were loaded
22649          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22650          */
22651         beforeloadadd : true,
22652         /**
22653          * @event load
22654          * Fires after a new set of Records has been loaded, before they are added to the store.
22655          * @param {Store} this
22656          * @param {Roo.data.Record[]} records The Records that were loaded
22657          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22658          * @params {Object} return from reader
22659          */
22660         load : true,
22661         /**
22662          * @event loadexception
22663          * Fires if an exception occurs in the Proxy during loading.
22664          * Called with the signature of the Proxy's "loadexception" event.
22665          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22666          * 
22667          * @param {Proxy} 
22668          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22669          * @param {Object} load options 
22670          * @param {Object} jsonData from your request (normally this contains the Exception)
22671          */
22672         loadexception : true
22673     });
22674     
22675     if(this.proxy){
22676         this.proxy = Roo.factory(this.proxy, Roo.data);
22677         this.proxy.xmodule = this.xmodule || false;
22678         this.relayEvents(this.proxy,  ["loadexception"]);
22679     }
22680     this.sortToggle = {};
22681     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22682
22683     Roo.data.Store.superclass.constructor.call(this);
22684
22685     if(this.inlineData){
22686         this.loadData(this.inlineData);
22687         delete this.inlineData;
22688     }
22689 };
22690
22691 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22692      /**
22693     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22694     * without a remote query - used by combo/forms at present.
22695     */
22696     
22697     /**
22698     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22699     */
22700     /**
22701     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22702     */
22703     /**
22704     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22705     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22706     */
22707     /**
22708     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22709     * on any HTTP request
22710     */
22711     /**
22712     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22713     */
22714     /**
22715     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22716     */
22717     multiSort: false,
22718     /**
22719     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22720     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22721     */
22722     remoteSort : false,
22723
22724     /**
22725     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22726      * loaded or when a record is removed. (defaults to false).
22727     */
22728     pruneModifiedRecords : false,
22729
22730     // private
22731     lastOptions : null,
22732
22733     /**
22734      * Add Records to the Store and fires the add event.
22735      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22736      */
22737     add : function(records){
22738         records = [].concat(records);
22739         for(var i = 0, len = records.length; i < len; i++){
22740             records[i].join(this);
22741         }
22742         var index = this.data.length;
22743         this.data.addAll(records);
22744         this.fireEvent("add", this, records, index);
22745     },
22746
22747     /**
22748      * Remove a Record from the Store and fires the remove event.
22749      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22750      */
22751     remove : function(record){
22752         var index = this.data.indexOf(record);
22753         this.data.removeAt(index);
22754         if(this.pruneModifiedRecords){
22755             this.modified.remove(record);
22756         }
22757         this.fireEvent("remove", this, record, index);
22758     },
22759
22760     /**
22761      * Remove all Records from the Store and fires the clear event.
22762      */
22763     removeAll : function(){
22764         this.data.clear();
22765         if(this.pruneModifiedRecords){
22766             this.modified = [];
22767         }
22768         this.fireEvent("clear", this);
22769     },
22770
22771     /**
22772      * Inserts Records to the Store at the given index and fires the add event.
22773      * @param {Number} index The start index at which to insert the passed Records.
22774      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22775      */
22776     insert : function(index, records){
22777         records = [].concat(records);
22778         for(var i = 0, len = records.length; i < len; i++){
22779             this.data.insert(index, records[i]);
22780             records[i].join(this);
22781         }
22782         this.fireEvent("add", this, records, index);
22783     },
22784
22785     /**
22786      * Get the index within the cache of the passed Record.
22787      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22788      * @return {Number} The index of the passed Record. Returns -1 if not found.
22789      */
22790     indexOf : function(record){
22791         return this.data.indexOf(record);
22792     },
22793
22794     /**
22795      * Get the index within the cache of the Record with the passed id.
22796      * @param {String} id The id of the Record to find.
22797      * @return {Number} The index of the Record. Returns -1 if not found.
22798      */
22799     indexOfId : function(id){
22800         return this.data.indexOfKey(id);
22801     },
22802
22803     /**
22804      * Get the Record with the specified id.
22805      * @param {String} id The id of the Record to find.
22806      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22807      */
22808     getById : function(id){
22809         return this.data.key(id);
22810     },
22811
22812     /**
22813      * Get the Record at the specified index.
22814      * @param {Number} index The index of the Record to find.
22815      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22816      */
22817     getAt : function(index){
22818         return this.data.itemAt(index);
22819     },
22820
22821     /**
22822      * Returns a range of Records between specified indices.
22823      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22824      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22825      * @return {Roo.data.Record[]} An array of Records
22826      */
22827     getRange : function(start, end){
22828         return this.data.getRange(start, end);
22829     },
22830
22831     // private
22832     storeOptions : function(o){
22833         o = Roo.apply({}, o);
22834         delete o.callback;
22835         delete o.scope;
22836         this.lastOptions = o;
22837     },
22838
22839     /**
22840      * Loads the Record cache from the configured Proxy using the configured Reader.
22841      * <p>
22842      * If using remote paging, then the first load call must specify the <em>start</em>
22843      * and <em>limit</em> properties in the options.params property to establish the initial
22844      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22845      * <p>
22846      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22847      * and this call will return before the new data has been loaded. Perform any post-processing
22848      * in a callback function, or in a "load" event handler.</strong>
22849      * <p>
22850      * @param {Object} options An object containing properties which control loading options:<ul>
22851      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22852      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22853      * passed the following arguments:<ul>
22854      * <li>r : Roo.data.Record[]</li>
22855      * <li>options: Options object from the load call</li>
22856      * <li>success: Boolean success indicator</li></ul></li>
22857      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22858      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22859      * </ul>
22860      */
22861     load : function(options){
22862         options = options || {};
22863         if(this.fireEvent("beforeload", this, options) !== false){
22864             this.storeOptions(options);
22865             var p = Roo.apply(options.params || {}, this.baseParams);
22866             // if meta was not loaded from remote source.. try requesting it.
22867             if (!this.reader.metaFromRemote) {
22868                 p._requestMeta = 1;
22869             }
22870             if(this.sortInfo && this.remoteSort){
22871                 var pn = this.paramNames;
22872                 p[pn["sort"]] = this.sortInfo.field;
22873                 p[pn["dir"]] = this.sortInfo.direction;
22874             }
22875             if (this.multiSort) {
22876                 var pn = this.paramNames;
22877                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
22878             }
22879             
22880             this.proxy.load(p, this.reader, this.loadRecords, this, options);
22881         }
22882     },
22883
22884     /**
22885      * Reloads the Record cache from the configured Proxy using the configured Reader and
22886      * the options from the last load operation performed.
22887      * @param {Object} options (optional) An object containing properties which may override the options
22888      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
22889      * the most recently used options are reused).
22890      */
22891     reload : function(options){
22892         this.load(Roo.applyIf(options||{}, this.lastOptions));
22893     },
22894
22895     // private
22896     // Called as a callback by the Reader during a load operation.
22897     loadRecords : function(o, options, success){
22898         if(!o || success === false){
22899             if(success !== false){
22900                 this.fireEvent("load", this, [], options, o);
22901             }
22902             if(options.callback){
22903                 options.callback.call(options.scope || this, [], options, false);
22904             }
22905             return;
22906         }
22907         // if data returned failure - throw an exception.
22908         if (o.success === false) {
22909             // show a message if no listener is registered.
22910             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
22911                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
22912             }
22913             // loadmask wil be hooked into this..
22914             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
22915             return;
22916         }
22917         var r = o.records, t = o.totalRecords || r.length;
22918         
22919         this.fireEvent("beforeloadadd", this, r, options, o);
22920         
22921         if(!options || options.add !== true){
22922             if(this.pruneModifiedRecords){
22923                 this.modified = [];
22924             }
22925             for(var i = 0, len = r.length; i < len; i++){
22926                 r[i].join(this);
22927             }
22928             if(this.snapshot){
22929                 this.data = this.snapshot;
22930                 delete this.snapshot;
22931             }
22932             this.data.clear();
22933             this.data.addAll(r);
22934             this.totalLength = t;
22935             this.applySort();
22936             this.fireEvent("datachanged", this);
22937         }else{
22938             this.totalLength = Math.max(t, this.data.length+r.length);
22939             this.add(r);
22940         }
22941         this.fireEvent("load", this, r, options, o);
22942         if(options.callback){
22943             options.callback.call(options.scope || this, r, options, true);
22944         }
22945     },
22946
22947
22948     /**
22949      * Loads data from a passed data block. A Reader which understands the format of the data
22950      * must have been configured in the constructor.
22951      * @param {Object} data The data block from which to read the Records.  The format of the data expected
22952      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
22953      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
22954      */
22955     loadData : function(o, append){
22956         var r = this.reader.readRecords(o);
22957         this.loadRecords(r, {add: append}, true);
22958     },
22959
22960     /**
22961      * Gets the number of cached records.
22962      * <p>
22963      * <em>If using paging, this may not be the total size of the dataset. If the data object
22964      * used by the Reader contains the dataset size, then the getTotalCount() function returns
22965      * the data set size</em>
22966      */
22967     getCount : function(){
22968         return this.data.length || 0;
22969     },
22970
22971     /**
22972      * Gets the total number of records in the dataset as returned by the server.
22973      * <p>
22974      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
22975      * the dataset size</em>
22976      */
22977     getTotalCount : function(){
22978         return this.totalLength || 0;
22979     },
22980
22981     /**
22982      * Returns the sort state of the Store as an object with two properties:
22983      * <pre><code>
22984  field {String} The name of the field by which the Records are sorted
22985  direction {String} The sort order, "ASC" or "DESC"
22986      * </code></pre>
22987      */
22988     getSortState : function(){
22989         return this.sortInfo;
22990     },
22991
22992     // private
22993     applySort : function(){
22994         if(this.sortInfo && !this.remoteSort){
22995             var s = this.sortInfo, f = s.field;
22996             var st = this.fields.get(f).sortType;
22997             var fn = function(r1, r2){
22998                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
22999                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23000             };
23001             this.data.sort(s.direction, fn);
23002             if(this.snapshot && this.snapshot != this.data){
23003                 this.snapshot.sort(s.direction, fn);
23004             }
23005         }
23006     },
23007
23008     /**
23009      * Sets the default sort column and order to be used by the next load operation.
23010      * @param {String} fieldName The name of the field to sort by.
23011      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23012      */
23013     setDefaultSort : function(field, dir){
23014         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23015     },
23016
23017     /**
23018      * Sort the Records.
23019      * If remote sorting is used, the sort is performed on the server, and the cache is
23020      * reloaded. If local sorting is used, the cache is sorted internally.
23021      * @param {String} fieldName The name of the field to sort by.
23022      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23023      */
23024     sort : function(fieldName, dir){
23025         var f = this.fields.get(fieldName);
23026         if(!dir){
23027             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23028             
23029             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23030                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23031             }else{
23032                 dir = f.sortDir;
23033             }
23034         }
23035         this.sortToggle[f.name] = dir;
23036         this.sortInfo = {field: f.name, direction: dir};
23037         if(!this.remoteSort){
23038             this.applySort();
23039             this.fireEvent("datachanged", this);
23040         }else{
23041             this.load(this.lastOptions);
23042         }
23043     },
23044
23045     /**
23046      * Calls the specified function for each of the Records in the cache.
23047      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23048      * Returning <em>false</em> aborts and exits the iteration.
23049      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23050      */
23051     each : function(fn, scope){
23052         this.data.each(fn, scope);
23053     },
23054
23055     /**
23056      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23057      * (e.g., during paging).
23058      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23059      */
23060     getModifiedRecords : function(){
23061         return this.modified;
23062     },
23063
23064     // private
23065     createFilterFn : function(property, value, anyMatch){
23066         if(!value.exec){ // not a regex
23067             value = String(value);
23068             if(value.length == 0){
23069                 return false;
23070             }
23071             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23072         }
23073         return function(r){
23074             return value.test(r.data[property]);
23075         };
23076     },
23077
23078     /**
23079      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23080      * @param {String} property A field on your records
23081      * @param {Number} start The record index to start at (defaults to 0)
23082      * @param {Number} end The last record index to include (defaults to length - 1)
23083      * @return {Number} The sum
23084      */
23085     sum : function(property, start, end){
23086         var rs = this.data.items, v = 0;
23087         start = start || 0;
23088         end = (end || end === 0) ? end : rs.length-1;
23089
23090         for(var i = start; i <= end; i++){
23091             v += (rs[i].data[property] || 0);
23092         }
23093         return v;
23094     },
23095
23096     /**
23097      * Filter the records by a specified property.
23098      * @param {String} field A field on your records
23099      * @param {String/RegExp} value Either a string that the field
23100      * should start with or a RegExp to test against the field
23101      * @param {Boolean} anyMatch True to match any part not just the beginning
23102      */
23103     filter : function(property, value, anyMatch){
23104         var fn = this.createFilterFn(property, value, anyMatch);
23105         return fn ? this.filterBy(fn) : this.clearFilter();
23106     },
23107
23108     /**
23109      * Filter by a function. The specified function will be called with each
23110      * record in this data source. If the function returns true the record is included,
23111      * otherwise it is filtered.
23112      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23113      * @param {Object} scope (optional) The scope of the function (defaults to this)
23114      */
23115     filterBy : function(fn, scope){
23116         this.snapshot = this.snapshot || this.data;
23117         this.data = this.queryBy(fn, scope||this);
23118         this.fireEvent("datachanged", this);
23119     },
23120
23121     /**
23122      * Query the records by a specified property.
23123      * @param {String} field A field on your records
23124      * @param {String/RegExp} value Either a string that the field
23125      * should start with or a RegExp to test against the field
23126      * @param {Boolean} anyMatch True to match any part not just the beginning
23127      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23128      */
23129     query : function(property, value, anyMatch){
23130         var fn = this.createFilterFn(property, value, anyMatch);
23131         return fn ? this.queryBy(fn) : this.data.clone();
23132     },
23133
23134     /**
23135      * Query by a function. The specified function will be called with each
23136      * record in this data source. If the function returns true the record is included
23137      * in the results.
23138      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23139      * @param {Object} scope (optional) The scope of the function (defaults to this)
23140       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23141      **/
23142     queryBy : function(fn, scope){
23143         var data = this.snapshot || this.data;
23144         return data.filterBy(fn, scope||this);
23145     },
23146
23147     /**
23148      * Collects unique values for a particular dataIndex from this store.
23149      * @param {String} dataIndex The property to collect
23150      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23151      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23152      * @return {Array} An array of the unique values
23153      **/
23154     collect : function(dataIndex, allowNull, bypassFilter){
23155         var d = (bypassFilter === true && this.snapshot) ?
23156                 this.snapshot.items : this.data.items;
23157         var v, sv, r = [], l = {};
23158         for(var i = 0, len = d.length; i < len; i++){
23159             v = d[i].data[dataIndex];
23160             sv = String(v);
23161             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23162                 l[sv] = true;
23163                 r[r.length] = v;
23164             }
23165         }
23166         return r;
23167     },
23168
23169     /**
23170      * Revert to a view of the Record cache with no filtering applied.
23171      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23172      */
23173     clearFilter : function(suppressEvent){
23174         if(this.snapshot && this.snapshot != this.data){
23175             this.data = this.snapshot;
23176             delete this.snapshot;
23177             if(suppressEvent !== true){
23178                 this.fireEvent("datachanged", this);
23179             }
23180         }
23181     },
23182
23183     // private
23184     afterEdit : function(record){
23185         if(this.modified.indexOf(record) == -1){
23186             this.modified.push(record);
23187         }
23188         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23189     },
23190     
23191     // private
23192     afterReject : function(record){
23193         this.modified.remove(record);
23194         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23195     },
23196
23197     // private
23198     afterCommit : function(record){
23199         this.modified.remove(record);
23200         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23201     },
23202
23203     /**
23204      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23205      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23206      */
23207     commitChanges : function(){
23208         var m = this.modified.slice(0);
23209         this.modified = [];
23210         for(var i = 0, len = m.length; i < len; i++){
23211             m[i].commit();
23212         }
23213     },
23214
23215     /**
23216      * Cancel outstanding changes on all changed records.
23217      */
23218     rejectChanges : function(){
23219         var m = this.modified.slice(0);
23220         this.modified = [];
23221         for(var i = 0, len = m.length; i < len; i++){
23222             m[i].reject();
23223         }
23224     },
23225
23226     onMetaChange : function(meta, rtype, o){
23227         this.recordType = rtype;
23228         this.fields = rtype.prototype.fields;
23229         delete this.snapshot;
23230         this.sortInfo = meta.sortInfo || this.sortInfo;
23231         this.modified = [];
23232         this.fireEvent('metachange', this, this.reader.meta);
23233     },
23234     
23235     moveIndex : function(data, type)
23236     {
23237         var index = this.indexOf(data);
23238         
23239         var newIndex = index + type;
23240         
23241         this.remove(data);
23242         
23243         this.insert(newIndex, data);
23244         
23245     }
23246 });/*
23247  * Based on:
23248  * Ext JS Library 1.1.1
23249  * Copyright(c) 2006-2007, Ext JS, LLC.
23250  *
23251  * Originally Released Under LGPL - original licence link has changed is not relivant.
23252  *
23253  * Fork - LGPL
23254  * <script type="text/javascript">
23255  */
23256
23257 /**
23258  * @class Roo.data.SimpleStore
23259  * @extends Roo.data.Store
23260  * Small helper class to make creating Stores from Array data easier.
23261  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23262  * @cfg {Array} fields An array of field definition objects, or field name strings.
23263  * @cfg {Array} data The multi-dimensional array of data
23264  * @constructor
23265  * @param {Object} config
23266  */
23267 Roo.data.SimpleStore = function(config){
23268     Roo.data.SimpleStore.superclass.constructor.call(this, {
23269         isLocal : true,
23270         reader: new Roo.data.ArrayReader({
23271                 id: config.id
23272             },
23273             Roo.data.Record.create(config.fields)
23274         ),
23275         proxy : new Roo.data.MemoryProxy(config.data)
23276     });
23277     this.load();
23278 };
23279 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23280  * Based on:
23281  * Ext JS Library 1.1.1
23282  * Copyright(c) 2006-2007, Ext JS, LLC.
23283  *
23284  * Originally Released Under LGPL - original licence link has changed is not relivant.
23285  *
23286  * Fork - LGPL
23287  * <script type="text/javascript">
23288  */
23289
23290 /**
23291 /**
23292  * @extends Roo.data.Store
23293  * @class Roo.data.JsonStore
23294  * Small helper class to make creating Stores for JSON data easier. <br/>
23295 <pre><code>
23296 var store = new Roo.data.JsonStore({
23297     url: 'get-images.php',
23298     root: 'images',
23299     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23300 });
23301 </code></pre>
23302  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23303  * JsonReader and HttpProxy (unless inline data is provided).</b>
23304  * @cfg {Array} fields An array of field definition objects, or field name strings.
23305  * @constructor
23306  * @param {Object} config
23307  */
23308 Roo.data.JsonStore = function(c){
23309     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23310         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23311         reader: new Roo.data.JsonReader(c, c.fields)
23312     }));
23313 };
23314 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23315  * Based on:
23316  * Ext JS Library 1.1.1
23317  * Copyright(c) 2006-2007, Ext JS, LLC.
23318  *
23319  * Originally Released Under LGPL - original licence link has changed is not relivant.
23320  *
23321  * Fork - LGPL
23322  * <script type="text/javascript">
23323  */
23324
23325  
23326 Roo.data.Field = function(config){
23327     if(typeof config == "string"){
23328         config = {name: config};
23329     }
23330     Roo.apply(this, config);
23331     
23332     if(!this.type){
23333         this.type = "auto";
23334     }
23335     
23336     var st = Roo.data.SortTypes;
23337     // named sortTypes are supported, here we look them up
23338     if(typeof this.sortType == "string"){
23339         this.sortType = st[this.sortType];
23340     }
23341     
23342     // set default sortType for strings and dates
23343     if(!this.sortType){
23344         switch(this.type){
23345             case "string":
23346                 this.sortType = st.asUCString;
23347                 break;
23348             case "date":
23349                 this.sortType = st.asDate;
23350                 break;
23351             default:
23352                 this.sortType = st.none;
23353         }
23354     }
23355
23356     // define once
23357     var stripRe = /[\$,%]/g;
23358
23359     // prebuilt conversion function for this field, instead of
23360     // switching every time we're reading a value
23361     if(!this.convert){
23362         var cv, dateFormat = this.dateFormat;
23363         switch(this.type){
23364             case "":
23365             case "auto":
23366             case undefined:
23367                 cv = function(v){ return v; };
23368                 break;
23369             case "string":
23370                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23371                 break;
23372             case "int":
23373                 cv = function(v){
23374                     return v !== undefined && v !== null && v !== '' ?
23375                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23376                     };
23377                 break;
23378             case "float":
23379                 cv = function(v){
23380                     return v !== undefined && v !== null && v !== '' ?
23381                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23382                     };
23383                 break;
23384             case "bool":
23385             case "boolean":
23386                 cv = function(v){ return v === true || v === "true" || v == 1; };
23387                 break;
23388             case "date":
23389                 cv = function(v){
23390                     if(!v){
23391                         return '';
23392                     }
23393                     if(v instanceof Date){
23394                         return v;
23395                     }
23396                     if(dateFormat){
23397                         if(dateFormat == "timestamp"){
23398                             return new Date(v*1000);
23399                         }
23400                         return Date.parseDate(v, dateFormat);
23401                     }
23402                     var parsed = Date.parse(v);
23403                     return parsed ? new Date(parsed) : null;
23404                 };
23405              break;
23406             
23407         }
23408         this.convert = cv;
23409     }
23410 };
23411
23412 Roo.data.Field.prototype = {
23413     dateFormat: null,
23414     defaultValue: "",
23415     mapping: null,
23416     sortType : null,
23417     sortDir : "ASC"
23418 };/*
23419  * Based on:
23420  * Ext JS Library 1.1.1
23421  * Copyright(c) 2006-2007, Ext JS, LLC.
23422  *
23423  * Originally Released Under LGPL - original licence link has changed is not relivant.
23424  *
23425  * Fork - LGPL
23426  * <script type="text/javascript">
23427  */
23428  
23429 // Base class for reading structured data from a data source.  This class is intended to be
23430 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23431
23432 /**
23433  * @class Roo.data.DataReader
23434  * Base class for reading structured data from a data source.  This class is intended to be
23435  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23436  */
23437
23438 Roo.data.DataReader = function(meta, recordType){
23439     
23440     this.meta = meta;
23441     
23442     this.recordType = recordType instanceof Array ? 
23443         Roo.data.Record.create(recordType) : recordType;
23444 };
23445
23446 Roo.data.DataReader.prototype = {
23447      /**
23448      * Create an empty record
23449      * @param {Object} data (optional) - overlay some values
23450      * @return {Roo.data.Record} record created.
23451      */
23452     newRow :  function(d) {
23453         var da =  {};
23454         this.recordType.prototype.fields.each(function(c) {
23455             switch( c.type) {
23456                 case 'int' : da[c.name] = 0; break;
23457                 case 'date' : da[c.name] = new Date(); break;
23458                 case 'float' : da[c.name] = 0.0; break;
23459                 case 'boolean' : da[c.name] = false; break;
23460                 default : da[c.name] = ""; break;
23461             }
23462             
23463         });
23464         return new this.recordType(Roo.apply(da, d));
23465     }
23466     
23467 };/*
23468  * Based on:
23469  * Ext JS Library 1.1.1
23470  * Copyright(c) 2006-2007, Ext JS, LLC.
23471  *
23472  * Originally Released Under LGPL - original licence link has changed is not relivant.
23473  *
23474  * Fork - LGPL
23475  * <script type="text/javascript">
23476  */
23477
23478 /**
23479  * @class Roo.data.DataProxy
23480  * @extends Roo.data.Observable
23481  * This class is an abstract base class for implementations which provide retrieval of
23482  * unformatted data objects.<br>
23483  * <p>
23484  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23485  * (of the appropriate type which knows how to parse the data object) to provide a block of
23486  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23487  * <p>
23488  * Custom implementations must implement the load method as described in
23489  * {@link Roo.data.HttpProxy#load}.
23490  */
23491 Roo.data.DataProxy = function(){
23492     this.addEvents({
23493         /**
23494          * @event beforeload
23495          * Fires before a network request is made to retrieve a data object.
23496          * @param {Object} This DataProxy object.
23497          * @param {Object} params The params parameter to the load function.
23498          */
23499         beforeload : true,
23500         /**
23501          * @event load
23502          * Fires before the load method's callback is called.
23503          * @param {Object} This DataProxy object.
23504          * @param {Object} o The data object.
23505          * @param {Object} arg The callback argument object passed to the load function.
23506          */
23507         load : true,
23508         /**
23509          * @event loadexception
23510          * Fires if an Exception occurs during data retrieval.
23511          * @param {Object} This DataProxy object.
23512          * @param {Object} o The data object.
23513          * @param {Object} arg The callback argument object passed to the load function.
23514          * @param {Object} e The Exception.
23515          */
23516         loadexception : true
23517     });
23518     Roo.data.DataProxy.superclass.constructor.call(this);
23519 };
23520
23521 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23522
23523     /**
23524      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23525      */
23526 /*
23527  * Based on:
23528  * Ext JS Library 1.1.1
23529  * Copyright(c) 2006-2007, Ext JS, LLC.
23530  *
23531  * Originally Released Under LGPL - original licence link has changed is not relivant.
23532  *
23533  * Fork - LGPL
23534  * <script type="text/javascript">
23535  */
23536 /**
23537  * @class Roo.data.MemoryProxy
23538  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23539  * to the Reader when its load method is called.
23540  * @constructor
23541  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23542  */
23543 Roo.data.MemoryProxy = function(data){
23544     if (data.data) {
23545         data = data.data;
23546     }
23547     Roo.data.MemoryProxy.superclass.constructor.call(this);
23548     this.data = data;
23549 };
23550
23551 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23552     /**
23553      * Load data from the requested source (in this case an in-memory
23554      * data object passed to the constructor), read the data object into
23555      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23556      * process that block using the passed callback.
23557      * @param {Object} params This parameter is not used by the MemoryProxy class.
23558      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23559      * object into a block of Roo.data.Records.
23560      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23561      * The function must be passed <ul>
23562      * <li>The Record block object</li>
23563      * <li>The "arg" argument from the load function</li>
23564      * <li>A boolean success indicator</li>
23565      * </ul>
23566      * @param {Object} scope The scope in which to call the callback
23567      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23568      */
23569     load : function(params, reader, callback, scope, arg){
23570         params = params || {};
23571         var result;
23572         try {
23573             result = reader.readRecords(this.data);
23574         }catch(e){
23575             this.fireEvent("loadexception", this, arg, null, e);
23576             callback.call(scope, null, arg, false);
23577             return;
23578         }
23579         callback.call(scope, result, arg, true);
23580     },
23581     
23582     // private
23583     update : function(params, records){
23584         
23585     }
23586 });/*
23587  * Based on:
23588  * Ext JS Library 1.1.1
23589  * Copyright(c) 2006-2007, Ext JS, LLC.
23590  *
23591  * Originally Released Under LGPL - original licence link has changed is not relivant.
23592  *
23593  * Fork - LGPL
23594  * <script type="text/javascript">
23595  */
23596 /**
23597  * @class Roo.data.HttpProxy
23598  * @extends Roo.data.DataProxy
23599  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23600  * configured to reference a certain URL.<br><br>
23601  * <p>
23602  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23603  * from which the running page was served.<br><br>
23604  * <p>
23605  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23606  * <p>
23607  * Be aware that to enable the browser to parse an XML document, the server must set
23608  * the Content-Type header in the HTTP response to "text/xml".
23609  * @constructor
23610  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23611  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23612  * will be used to make the request.
23613  */
23614 Roo.data.HttpProxy = function(conn){
23615     Roo.data.HttpProxy.superclass.constructor.call(this);
23616     // is conn a conn config or a real conn?
23617     this.conn = conn;
23618     this.useAjax = !conn || !conn.events;
23619   
23620 };
23621
23622 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23623     // thse are take from connection...
23624     
23625     /**
23626      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23627      */
23628     /**
23629      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23630      * extra parameters to each request made by this object. (defaults to undefined)
23631      */
23632     /**
23633      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23634      *  to each request made by this object. (defaults to undefined)
23635      */
23636     /**
23637      * @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)
23638      */
23639     /**
23640      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23641      */
23642      /**
23643      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23644      * @type Boolean
23645      */
23646   
23647
23648     /**
23649      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23650      * @type Boolean
23651      */
23652     /**
23653      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23654      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23655      * a finer-grained basis than the DataProxy events.
23656      */
23657     getConnection : function(){
23658         return this.useAjax ? Roo.Ajax : this.conn;
23659     },
23660
23661     /**
23662      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23663      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23664      * process that block using the passed callback.
23665      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23666      * for the request to the remote server.
23667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23668      * object into a block of Roo.data.Records.
23669      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23670      * The function must be passed <ul>
23671      * <li>The Record block object</li>
23672      * <li>The "arg" argument from the load function</li>
23673      * <li>A boolean success indicator</li>
23674      * </ul>
23675      * @param {Object} scope The scope in which to call the callback
23676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23677      */
23678     load : function(params, reader, callback, scope, arg){
23679         if(this.fireEvent("beforeload", this, params) !== false){
23680             var  o = {
23681                 params : params || {},
23682                 request: {
23683                     callback : callback,
23684                     scope : scope,
23685                     arg : arg
23686                 },
23687                 reader: reader,
23688                 callback : this.loadResponse,
23689                 scope: this
23690             };
23691             if(this.useAjax){
23692                 Roo.applyIf(o, this.conn);
23693                 if(this.activeRequest){
23694                     Roo.Ajax.abort(this.activeRequest);
23695                 }
23696                 this.activeRequest = Roo.Ajax.request(o);
23697             }else{
23698                 this.conn.request(o);
23699             }
23700         }else{
23701             callback.call(scope||this, null, arg, false);
23702         }
23703     },
23704
23705     // private
23706     loadResponse : function(o, success, response){
23707         delete this.activeRequest;
23708         if(!success){
23709             this.fireEvent("loadexception", this, o, response);
23710             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23711             return;
23712         }
23713         var result;
23714         try {
23715             result = o.reader.read(response);
23716         }catch(e){
23717             this.fireEvent("loadexception", this, o, response, e);
23718             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23719             return;
23720         }
23721         
23722         this.fireEvent("load", this, o, o.request.arg);
23723         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23724     },
23725
23726     // private
23727     update : function(dataSet){
23728
23729     },
23730
23731     // private
23732     updateResponse : function(dataSet){
23733
23734     }
23735 });/*
23736  * Based on:
23737  * Ext JS Library 1.1.1
23738  * Copyright(c) 2006-2007, Ext JS, LLC.
23739  *
23740  * Originally Released Under LGPL - original licence link has changed is not relivant.
23741  *
23742  * Fork - LGPL
23743  * <script type="text/javascript">
23744  */
23745
23746 /**
23747  * @class Roo.data.ScriptTagProxy
23748  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23749  * other than the originating domain of the running page.<br><br>
23750  * <p>
23751  * <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
23752  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23753  * <p>
23754  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23755  * source code that is used as the source inside a &lt;script> tag.<br><br>
23756  * <p>
23757  * In order for the browser to process the returned data, the server must wrap the data object
23758  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23759  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23760  * depending on whether the callback name was passed:
23761  * <p>
23762  * <pre><code>
23763 boolean scriptTag = false;
23764 String cb = request.getParameter("callback");
23765 if (cb != null) {
23766     scriptTag = true;
23767     response.setContentType("text/javascript");
23768 } else {
23769     response.setContentType("application/x-json");
23770 }
23771 Writer out = response.getWriter();
23772 if (scriptTag) {
23773     out.write(cb + "(");
23774 }
23775 out.print(dataBlock.toJsonString());
23776 if (scriptTag) {
23777     out.write(");");
23778 }
23779 </pre></code>
23780  *
23781  * @constructor
23782  * @param {Object} config A configuration object.
23783  */
23784 Roo.data.ScriptTagProxy = function(config){
23785     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23786     Roo.apply(this, config);
23787     this.head = document.getElementsByTagName("head")[0];
23788 };
23789
23790 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23791
23792 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23793     /**
23794      * @cfg {String} url The URL from which to request the data object.
23795      */
23796     /**
23797      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23798      */
23799     timeout : 30000,
23800     /**
23801      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23802      * the server the name of the callback function set up by the load call to process the returned data object.
23803      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23804      * javascript output which calls this named function passing the data object as its only parameter.
23805      */
23806     callbackParam : "callback",
23807     /**
23808      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23809      * name to the request.
23810      */
23811     nocache : true,
23812
23813     /**
23814      * Load data from the configured URL, read the data object into
23815      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23816      * process that block using the passed callback.
23817      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23818      * for the request to the remote server.
23819      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23820      * object into a block of Roo.data.Records.
23821      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23822      * The function must be passed <ul>
23823      * <li>The Record block object</li>
23824      * <li>The "arg" argument from the load function</li>
23825      * <li>A boolean success indicator</li>
23826      * </ul>
23827      * @param {Object} scope The scope in which to call the callback
23828      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23829      */
23830     load : function(params, reader, callback, scope, arg){
23831         if(this.fireEvent("beforeload", this, params) !== false){
23832
23833             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23834
23835             var url = this.url;
23836             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23837             if(this.nocache){
23838                 url += "&_dc=" + (new Date().getTime());
23839             }
23840             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23841             var trans = {
23842                 id : transId,
23843                 cb : "stcCallback"+transId,
23844                 scriptId : "stcScript"+transId,
23845                 params : params,
23846                 arg : arg,
23847                 url : url,
23848                 callback : callback,
23849                 scope : scope,
23850                 reader : reader
23851             };
23852             var conn = this;
23853
23854             window[trans.cb] = function(o){
23855                 conn.handleResponse(o, trans);
23856             };
23857
23858             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23859
23860             if(this.autoAbort !== false){
23861                 this.abort();
23862             }
23863
23864             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
23865
23866             var script = document.createElement("script");
23867             script.setAttribute("src", url);
23868             script.setAttribute("type", "text/javascript");
23869             script.setAttribute("id", trans.scriptId);
23870             this.head.appendChild(script);
23871
23872             this.trans = trans;
23873         }else{
23874             callback.call(scope||this, null, arg, false);
23875         }
23876     },
23877
23878     // private
23879     isLoading : function(){
23880         return this.trans ? true : false;
23881     },
23882
23883     /**
23884      * Abort the current server request.
23885      */
23886     abort : function(){
23887         if(this.isLoading()){
23888             this.destroyTrans(this.trans);
23889         }
23890     },
23891
23892     // private
23893     destroyTrans : function(trans, isLoaded){
23894         this.head.removeChild(document.getElementById(trans.scriptId));
23895         clearTimeout(trans.timeoutId);
23896         if(isLoaded){
23897             window[trans.cb] = undefined;
23898             try{
23899                 delete window[trans.cb];
23900             }catch(e){}
23901         }else{
23902             // if hasn't been loaded, wait for load to remove it to prevent script error
23903             window[trans.cb] = function(){
23904                 window[trans.cb] = undefined;
23905                 try{
23906                     delete window[trans.cb];
23907                 }catch(e){}
23908             };
23909         }
23910     },
23911
23912     // private
23913     handleResponse : function(o, trans){
23914         this.trans = false;
23915         this.destroyTrans(trans, true);
23916         var result;
23917         try {
23918             result = trans.reader.readRecords(o);
23919         }catch(e){
23920             this.fireEvent("loadexception", this, o, trans.arg, e);
23921             trans.callback.call(trans.scope||window, null, trans.arg, false);
23922             return;
23923         }
23924         this.fireEvent("load", this, o, trans.arg);
23925         trans.callback.call(trans.scope||window, result, trans.arg, true);
23926     },
23927
23928     // private
23929     handleFailure : function(trans){
23930         this.trans = false;
23931         this.destroyTrans(trans, false);
23932         this.fireEvent("loadexception", this, null, trans.arg);
23933         trans.callback.call(trans.scope||window, null, trans.arg, false);
23934     }
23935 });/*
23936  * Based on:
23937  * Ext JS Library 1.1.1
23938  * Copyright(c) 2006-2007, Ext JS, LLC.
23939  *
23940  * Originally Released Under LGPL - original licence link has changed is not relivant.
23941  *
23942  * Fork - LGPL
23943  * <script type="text/javascript">
23944  */
23945
23946 /**
23947  * @class Roo.data.JsonReader
23948  * @extends Roo.data.DataReader
23949  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
23950  * based on mappings in a provided Roo.data.Record constructor.
23951  * 
23952  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
23953  * in the reply previously. 
23954  * 
23955  * <p>
23956  * Example code:
23957  * <pre><code>
23958 var RecordDef = Roo.data.Record.create([
23959     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
23960     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
23961 ]);
23962 var myReader = new Roo.data.JsonReader({
23963     totalProperty: "results",    // The property which contains the total dataset size (optional)
23964     root: "rows",                // The property which contains an Array of row objects
23965     id: "id"                     // The property within each row object that provides an ID for the record (optional)
23966 }, RecordDef);
23967 </code></pre>
23968  * <p>
23969  * This would consume a JSON file like this:
23970  * <pre><code>
23971 { 'results': 2, 'rows': [
23972     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
23973     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
23974 }
23975 </code></pre>
23976  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
23977  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
23978  * paged from the remote server.
23979  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
23980  * @cfg {String} root name of the property which contains the Array of row objects.
23981  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
23982  * @cfg {Array} fields Array of field definition objects
23983  * @constructor
23984  * Create a new JsonReader
23985  * @param {Object} meta Metadata configuration options
23986  * @param {Object} recordType Either an Array of field definition objects,
23987  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
23988  */
23989 Roo.data.JsonReader = function(meta, recordType){
23990     
23991     meta = meta || {};
23992     // set some defaults:
23993     Roo.applyIf(meta, {
23994         totalProperty: 'total',
23995         successProperty : 'success',
23996         root : 'data',
23997         id : 'id'
23998     });
23999     
24000     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24001 };
24002 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24003     
24004     /**
24005      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24006      * Used by Store query builder to append _requestMeta to params.
24007      * 
24008      */
24009     metaFromRemote : false,
24010     /**
24011      * This method is only used by a DataProxy which has retrieved data from a remote server.
24012      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24013      * @return {Object} data A data block which is used by an Roo.data.Store object as
24014      * a cache of Roo.data.Records.
24015      */
24016     read : function(response){
24017         var json = response.responseText;
24018        
24019         var o = /* eval:var:o */ eval("("+json+")");
24020         if(!o) {
24021             throw {message: "JsonReader.read: Json object not found"};
24022         }
24023         
24024         if(o.metaData){
24025             
24026             delete this.ef;
24027             this.metaFromRemote = true;
24028             this.meta = o.metaData;
24029             this.recordType = Roo.data.Record.create(o.metaData.fields);
24030             this.onMetaChange(this.meta, this.recordType, o);
24031         }
24032         return this.readRecords(o);
24033     },
24034
24035     // private function a store will implement
24036     onMetaChange : function(meta, recordType, o){
24037
24038     },
24039
24040     /**
24041          * @ignore
24042          */
24043     simpleAccess: function(obj, subsc) {
24044         return obj[subsc];
24045     },
24046
24047         /**
24048          * @ignore
24049          */
24050     getJsonAccessor: function(){
24051         var re = /[\[\.]/;
24052         return function(expr) {
24053             try {
24054                 return(re.test(expr))
24055                     ? new Function("obj", "return obj." + expr)
24056                     : function(obj){
24057                         return obj[expr];
24058                     };
24059             } catch(e){}
24060             return Roo.emptyFn;
24061         };
24062     }(),
24063
24064     /**
24065      * Create a data block containing Roo.data.Records from an XML document.
24066      * @param {Object} o An object which contains an Array of row objects in the property specified
24067      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24068      * which contains the total size of the dataset.
24069      * @return {Object} data A data block which is used by an Roo.data.Store object as
24070      * a cache of Roo.data.Records.
24071      */
24072     readRecords : function(o){
24073         /**
24074          * After any data loads, the raw JSON data is available for further custom processing.
24075          * @type Object
24076          */
24077         this.o = o;
24078         var s = this.meta, Record = this.recordType,
24079             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24080
24081 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24082         if (!this.ef) {
24083             if(s.totalProperty) {
24084                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24085                 }
24086                 if(s.successProperty) {
24087                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24088                 }
24089                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24090                 if (s.id) {
24091                         var g = this.getJsonAccessor(s.id);
24092                         this.getId = function(rec) {
24093                                 var r = g(rec);  
24094                                 return (r === undefined || r === "") ? null : r;
24095                         };
24096                 } else {
24097                         this.getId = function(){return null;};
24098                 }
24099             this.ef = [];
24100             for(var jj = 0; jj < fl; jj++){
24101                 f = fi[jj];
24102                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24103                 this.ef[jj] = this.getJsonAccessor(map);
24104             }
24105         }
24106
24107         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24108         if(s.totalProperty){
24109             var vt = parseInt(this.getTotal(o), 10);
24110             if(!isNaN(vt)){
24111                 totalRecords = vt;
24112             }
24113         }
24114         if(s.successProperty){
24115             var vs = this.getSuccess(o);
24116             if(vs === false || vs === 'false'){
24117                 success = false;
24118             }
24119         }
24120         var records = [];
24121         for(var i = 0; i < c; i++){
24122                 var n = root[i];
24123             var values = {};
24124             var id = this.getId(n);
24125             for(var j = 0; j < fl; j++){
24126                 f = fi[j];
24127             var v = this.ef[j](n);
24128             if (!f.convert) {
24129                 Roo.log('missing convert for ' + f.name);
24130                 Roo.log(f);
24131                 continue;
24132             }
24133             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24134             }
24135             var record = new Record(values, id);
24136             record.json = n;
24137             records[i] = record;
24138         }
24139         return {
24140             raw : o,
24141             success : success,
24142             records : records,
24143             totalRecords : totalRecords
24144         };
24145     }
24146 });/*
24147  * Based on:
24148  * Ext JS Library 1.1.1
24149  * Copyright(c) 2006-2007, Ext JS, LLC.
24150  *
24151  * Originally Released Under LGPL - original licence link has changed is not relivant.
24152  *
24153  * Fork - LGPL
24154  * <script type="text/javascript">
24155  */
24156
24157 /**
24158  * @class Roo.data.XmlReader
24159  * @extends Roo.data.DataReader
24160  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24161  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24162  * <p>
24163  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24164  * header in the HTTP response must be set to "text/xml".</em>
24165  * <p>
24166  * Example code:
24167  * <pre><code>
24168 var RecordDef = Roo.data.Record.create([
24169    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24170    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24171 ]);
24172 var myReader = new Roo.data.XmlReader({
24173    totalRecords: "results", // The element which contains the total dataset size (optional)
24174    record: "row",           // The repeated element which contains row information
24175    id: "id"                 // The element within the row that provides an ID for the record (optional)
24176 }, RecordDef);
24177 </code></pre>
24178  * <p>
24179  * This would consume an XML file like this:
24180  * <pre><code>
24181 &lt;?xml?>
24182 &lt;dataset>
24183  &lt;results>2&lt;/results>
24184  &lt;row>
24185    &lt;id>1&lt;/id>
24186    &lt;name>Bill&lt;/name>
24187    &lt;occupation>Gardener&lt;/occupation>
24188  &lt;/row>
24189  &lt;row>
24190    &lt;id>2&lt;/id>
24191    &lt;name>Ben&lt;/name>
24192    &lt;occupation>Horticulturalist&lt;/occupation>
24193  &lt;/row>
24194 &lt;/dataset>
24195 </code></pre>
24196  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24197  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24198  * paged from the remote server.
24199  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24200  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24201  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24202  * a record identifier value.
24203  * @constructor
24204  * Create a new XmlReader
24205  * @param {Object} meta Metadata configuration options
24206  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24207  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24208  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24209  */
24210 Roo.data.XmlReader = function(meta, recordType){
24211     meta = meta || {};
24212     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24213 };
24214 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24215     /**
24216      * This method is only used by a DataProxy which has retrieved data from a remote server.
24217          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24218          * to contain a method called 'responseXML' that returns an XML document object.
24219      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24220      * a cache of Roo.data.Records.
24221      */
24222     read : function(response){
24223         var doc = response.responseXML;
24224         if(!doc) {
24225             throw {message: "XmlReader.read: XML Document not available"};
24226         }
24227         return this.readRecords(doc);
24228     },
24229
24230     /**
24231      * Create a data block containing Roo.data.Records from an XML document.
24232          * @param {Object} doc A parsed XML document.
24233      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24234      * a cache of Roo.data.Records.
24235      */
24236     readRecords : function(doc){
24237         /**
24238          * After any data loads/reads, the raw XML Document is available for further custom processing.
24239          * @type XMLDocument
24240          */
24241         this.xmlData = doc;
24242         var root = doc.documentElement || doc;
24243         var q = Roo.DomQuery;
24244         var recordType = this.recordType, fields = recordType.prototype.fields;
24245         var sid = this.meta.id;
24246         var totalRecords = 0, success = true;
24247         if(this.meta.totalRecords){
24248             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24249         }
24250         
24251         if(this.meta.success){
24252             var sv = q.selectValue(this.meta.success, root, true);
24253             success = sv !== false && sv !== 'false';
24254         }
24255         var records = [];
24256         var ns = q.select(this.meta.record, root);
24257         for(var i = 0, len = ns.length; i < len; i++) {
24258                 var n = ns[i];
24259                 var values = {};
24260                 var id = sid ? q.selectValue(sid, n) : undefined;
24261                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24262                     var f = fields.items[j];
24263                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24264                     v = f.convert(v);
24265                     values[f.name] = v;
24266                 }
24267                 var record = new recordType(values, id);
24268                 record.node = n;
24269                 records[records.length] = record;
24270             }
24271
24272             return {
24273                 success : success,
24274                 records : records,
24275                 totalRecords : totalRecords || records.length
24276             };
24277     }
24278 });/*
24279  * Based on:
24280  * Ext JS Library 1.1.1
24281  * Copyright(c) 2006-2007, Ext JS, LLC.
24282  *
24283  * Originally Released Under LGPL - original licence link has changed is not relivant.
24284  *
24285  * Fork - LGPL
24286  * <script type="text/javascript">
24287  */
24288
24289 /**
24290  * @class Roo.data.ArrayReader
24291  * @extends Roo.data.DataReader
24292  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24293  * Each element of that Array represents a row of data fields. The
24294  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24295  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24296  * <p>
24297  * Example code:.
24298  * <pre><code>
24299 var RecordDef = Roo.data.Record.create([
24300     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24301     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24302 ]);
24303 var myReader = new Roo.data.ArrayReader({
24304     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24305 }, RecordDef);
24306 </code></pre>
24307  * <p>
24308  * This would consume an Array like this:
24309  * <pre><code>
24310 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24311   </code></pre>
24312  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24313  * @constructor
24314  * Create a new JsonReader
24315  * @param {Object} meta Metadata configuration options.
24316  * @param {Object} recordType Either an Array of field definition objects
24317  * as specified to {@link Roo.data.Record#create},
24318  * or an {@link Roo.data.Record} object
24319  * created using {@link Roo.data.Record#create}.
24320  */
24321 Roo.data.ArrayReader = function(meta, recordType){
24322     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24323 };
24324
24325 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24326     /**
24327      * Create a data block containing Roo.data.Records from an XML document.
24328      * @param {Object} o An Array of row objects which represents the dataset.
24329      * @return {Object} data A data block which is used by an Roo.data.Store object as
24330      * a cache of Roo.data.Records.
24331      */
24332     readRecords : function(o){
24333         var sid = this.meta ? this.meta.id : null;
24334         var recordType = this.recordType, fields = recordType.prototype.fields;
24335         var records = [];
24336         var root = o;
24337             for(var i = 0; i < root.length; i++){
24338                     var n = root[i];
24339                 var values = {};
24340                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24341                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24342                 var f = fields.items[j];
24343                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24344                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24345                 v = f.convert(v);
24346                 values[f.name] = v;
24347             }
24348                 var record = new recordType(values, id);
24349                 record.json = n;
24350                 records[records.length] = record;
24351             }
24352             return {
24353                 records : records,
24354                 totalRecords : records.length
24355             };
24356     }
24357 });/*
24358  * Based on:
24359  * Ext JS Library 1.1.1
24360  * Copyright(c) 2006-2007, Ext JS, LLC.
24361  *
24362  * Originally Released Under LGPL - original licence link has changed is not relivant.
24363  *
24364  * Fork - LGPL
24365  * <script type="text/javascript">
24366  */
24367
24368
24369 /**
24370  * @class Roo.data.Tree
24371  * @extends Roo.util.Observable
24372  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24373  * in the tree have most standard DOM functionality.
24374  * @constructor
24375  * @param {Node} root (optional) The root node
24376  */
24377 Roo.data.Tree = function(root){
24378    this.nodeHash = {};
24379    /**
24380     * The root node for this tree
24381     * @type Node
24382     */
24383    this.root = null;
24384    if(root){
24385        this.setRootNode(root);
24386    }
24387    this.addEvents({
24388        /**
24389         * @event append
24390         * Fires when a new child node is appended to a node in this tree.
24391         * @param {Tree} tree The owner tree
24392         * @param {Node} parent The parent node
24393         * @param {Node} node The newly appended node
24394         * @param {Number} index The index of the newly appended node
24395         */
24396        "append" : true,
24397        /**
24398         * @event remove
24399         * Fires when a child node is removed from a node in this tree.
24400         * @param {Tree} tree The owner tree
24401         * @param {Node} parent The parent node
24402         * @param {Node} node The child node removed
24403         */
24404        "remove" : true,
24405        /**
24406         * @event move
24407         * Fires when a node is moved to a new location in the tree
24408         * @param {Tree} tree The owner tree
24409         * @param {Node} node The node moved
24410         * @param {Node} oldParent The old parent of this node
24411         * @param {Node} newParent The new parent of this node
24412         * @param {Number} index The index it was moved to
24413         */
24414        "move" : true,
24415        /**
24416         * @event insert
24417         * Fires when a new child node is inserted in a node in this tree.
24418         * @param {Tree} tree The owner tree
24419         * @param {Node} parent The parent node
24420         * @param {Node} node The child node inserted
24421         * @param {Node} refNode The child node the node was inserted before
24422         */
24423        "insert" : true,
24424        /**
24425         * @event beforeappend
24426         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24427         * @param {Tree} tree The owner tree
24428         * @param {Node} parent The parent node
24429         * @param {Node} node The child node to be appended
24430         */
24431        "beforeappend" : true,
24432        /**
24433         * @event beforeremove
24434         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24435         * @param {Tree} tree The owner tree
24436         * @param {Node} parent The parent node
24437         * @param {Node} node The child node to be removed
24438         */
24439        "beforeremove" : true,
24440        /**
24441         * @event beforemove
24442         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24443         * @param {Tree} tree The owner tree
24444         * @param {Node} node The node being moved
24445         * @param {Node} oldParent The parent of the node
24446         * @param {Node} newParent The new parent the node is moving to
24447         * @param {Number} index The index it is being moved to
24448         */
24449        "beforemove" : true,
24450        /**
24451         * @event beforeinsert
24452         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24453         * @param {Tree} tree The owner tree
24454         * @param {Node} parent The parent node
24455         * @param {Node} node The child node to be inserted
24456         * @param {Node} refNode The child node the node is being inserted before
24457         */
24458        "beforeinsert" : true
24459    });
24460
24461     Roo.data.Tree.superclass.constructor.call(this);
24462 };
24463
24464 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24465     pathSeparator: "/",
24466
24467     proxyNodeEvent : function(){
24468         return this.fireEvent.apply(this, arguments);
24469     },
24470
24471     /**
24472      * Returns the root node for this tree.
24473      * @return {Node}
24474      */
24475     getRootNode : function(){
24476         return this.root;
24477     },
24478
24479     /**
24480      * Sets the root node for this tree.
24481      * @param {Node} node
24482      * @return {Node}
24483      */
24484     setRootNode : function(node){
24485         this.root = node;
24486         node.ownerTree = this;
24487         node.isRoot = true;
24488         this.registerNode(node);
24489         return node;
24490     },
24491
24492     /**
24493      * Gets a node in this tree by its id.
24494      * @param {String} id
24495      * @return {Node}
24496      */
24497     getNodeById : function(id){
24498         return this.nodeHash[id];
24499     },
24500
24501     registerNode : function(node){
24502         this.nodeHash[node.id] = node;
24503     },
24504
24505     unregisterNode : function(node){
24506         delete this.nodeHash[node.id];
24507     },
24508
24509     toString : function(){
24510         return "[Tree"+(this.id?" "+this.id:"")+"]";
24511     }
24512 });
24513
24514 /**
24515  * @class Roo.data.Node
24516  * @extends Roo.util.Observable
24517  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24518  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24519  * @constructor
24520  * @param {Object} attributes The attributes/config for the node
24521  */
24522 Roo.data.Node = function(attributes){
24523     /**
24524      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24525      * @type {Object}
24526      */
24527     this.attributes = attributes || {};
24528     this.leaf = this.attributes.leaf;
24529     /**
24530      * The node id. @type String
24531      */
24532     this.id = this.attributes.id;
24533     if(!this.id){
24534         this.id = Roo.id(null, "ynode-");
24535         this.attributes.id = this.id;
24536     }
24537      
24538     
24539     /**
24540      * All child nodes of this node. @type Array
24541      */
24542     this.childNodes = [];
24543     if(!this.childNodes.indexOf){ // indexOf is a must
24544         this.childNodes.indexOf = function(o){
24545             for(var i = 0, len = this.length; i < len; i++){
24546                 if(this[i] == o) {
24547                     return i;
24548                 }
24549             }
24550             return -1;
24551         };
24552     }
24553     /**
24554      * The parent node for this node. @type Node
24555      */
24556     this.parentNode = null;
24557     /**
24558      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24559      */
24560     this.firstChild = null;
24561     /**
24562      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24563      */
24564     this.lastChild = null;
24565     /**
24566      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24567      */
24568     this.previousSibling = null;
24569     /**
24570      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24571      */
24572     this.nextSibling = null;
24573
24574     this.addEvents({
24575        /**
24576         * @event append
24577         * Fires when a new child node is appended
24578         * @param {Tree} tree The owner tree
24579         * @param {Node} this This node
24580         * @param {Node} node The newly appended node
24581         * @param {Number} index The index of the newly appended node
24582         */
24583        "append" : true,
24584        /**
24585         * @event remove
24586         * Fires when a child node is removed
24587         * @param {Tree} tree The owner tree
24588         * @param {Node} this This node
24589         * @param {Node} node The removed node
24590         */
24591        "remove" : true,
24592        /**
24593         * @event move
24594         * Fires when this node is moved to a new location in the tree
24595         * @param {Tree} tree The owner tree
24596         * @param {Node} this This node
24597         * @param {Node} oldParent The old parent of this node
24598         * @param {Node} newParent The new parent of this node
24599         * @param {Number} index The index it was moved to
24600         */
24601        "move" : true,
24602        /**
24603         * @event insert
24604         * Fires when a new child node is inserted.
24605         * @param {Tree} tree The owner tree
24606         * @param {Node} this This node
24607         * @param {Node} node The child node inserted
24608         * @param {Node} refNode The child node the node was inserted before
24609         */
24610        "insert" : true,
24611        /**
24612         * @event beforeappend
24613         * Fires before a new child is appended, return false to cancel the append.
24614         * @param {Tree} tree The owner tree
24615         * @param {Node} this This node
24616         * @param {Node} node The child node to be appended
24617         */
24618        "beforeappend" : true,
24619        /**
24620         * @event beforeremove
24621         * Fires before a child is removed, return false to cancel the remove.
24622         * @param {Tree} tree The owner tree
24623         * @param {Node} this This node
24624         * @param {Node} node The child node to be removed
24625         */
24626        "beforeremove" : true,
24627        /**
24628         * @event beforemove
24629         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24630         * @param {Tree} tree The owner tree
24631         * @param {Node} this This node
24632         * @param {Node} oldParent The parent of this node
24633         * @param {Node} newParent The new parent this node is moving to
24634         * @param {Number} index The index it is being moved to
24635         */
24636        "beforemove" : true,
24637        /**
24638         * @event beforeinsert
24639         * Fires before a new child is inserted, return false to cancel the insert.
24640         * @param {Tree} tree The owner tree
24641         * @param {Node} this This node
24642         * @param {Node} node The child node to be inserted
24643         * @param {Node} refNode The child node the node is being inserted before
24644         */
24645        "beforeinsert" : true
24646    });
24647     this.listeners = this.attributes.listeners;
24648     Roo.data.Node.superclass.constructor.call(this);
24649 };
24650
24651 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24652     fireEvent : function(evtName){
24653         // first do standard event for this node
24654         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24655             return false;
24656         }
24657         // then bubble it up to the tree if the event wasn't cancelled
24658         var ot = this.getOwnerTree();
24659         if(ot){
24660             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24661                 return false;
24662             }
24663         }
24664         return true;
24665     },
24666
24667     /**
24668      * Returns true if this node is a leaf
24669      * @return {Boolean}
24670      */
24671     isLeaf : function(){
24672         return this.leaf === true;
24673     },
24674
24675     // private
24676     setFirstChild : function(node){
24677         this.firstChild = node;
24678     },
24679
24680     //private
24681     setLastChild : function(node){
24682         this.lastChild = node;
24683     },
24684
24685
24686     /**
24687      * Returns true if this node is the last child of its parent
24688      * @return {Boolean}
24689      */
24690     isLast : function(){
24691        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24692     },
24693
24694     /**
24695      * Returns true if this node is the first child of its parent
24696      * @return {Boolean}
24697      */
24698     isFirst : function(){
24699        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24700     },
24701
24702     hasChildNodes : function(){
24703         return !this.isLeaf() && this.childNodes.length > 0;
24704     },
24705
24706     /**
24707      * Insert node(s) as the last child node of this node.
24708      * @param {Node/Array} node The node or Array of nodes to append
24709      * @return {Node} The appended node if single append, or null if an array was passed
24710      */
24711     appendChild : function(node){
24712         var multi = false;
24713         if(node instanceof Array){
24714             multi = node;
24715         }else if(arguments.length > 1){
24716             multi = arguments;
24717         }
24718         // if passed an array or multiple args do them one by one
24719         if(multi){
24720             for(var i = 0, len = multi.length; i < len; i++) {
24721                 this.appendChild(multi[i]);
24722             }
24723         }else{
24724             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24725                 return false;
24726             }
24727             var index = this.childNodes.length;
24728             var oldParent = node.parentNode;
24729             // it's a move, make sure we move it cleanly
24730             if(oldParent){
24731                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24732                     return false;
24733                 }
24734                 oldParent.removeChild(node);
24735             }
24736             index = this.childNodes.length;
24737             if(index == 0){
24738                 this.setFirstChild(node);
24739             }
24740             this.childNodes.push(node);
24741             node.parentNode = this;
24742             var ps = this.childNodes[index-1];
24743             if(ps){
24744                 node.previousSibling = ps;
24745                 ps.nextSibling = node;
24746             }else{
24747                 node.previousSibling = null;
24748             }
24749             node.nextSibling = null;
24750             this.setLastChild(node);
24751             node.setOwnerTree(this.getOwnerTree());
24752             this.fireEvent("append", this.ownerTree, this, node, index);
24753             if(oldParent){
24754                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24755             }
24756             return node;
24757         }
24758     },
24759
24760     /**
24761      * Removes a child node from this node.
24762      * @param {Node} node The node to remove
24763      * @return {Node} The removed node
24764      */
24765     removeChild : function(node){
24766         var index = this.childNodes.indexOf(node);
24767         if(index == -1){
24768             return false;
24769         }
24770         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24771             return false;
24772         }
24773
24774         // remove it from childNodes collection
24775         this.childNodes.splice(index, 1);
24776
24777         // update siblings
24778         if(node.previousSibling){
24779             node.previousSibling.nextSibling = node.nextSibling;
24780         }
24781         if(node.nextSibling){
24782             node.nextSibling.previousSibling = node.previousSibling;
24783         }
24784
24785         // update child refs
24786         if(this.firstChild == node){
24787             this.setFirstChild(node.nextSibling);
24788         }
24789         if(this.lastChild == node){
24790             this.setLastChild(node.previousSibling);
24791         }
24792
24793         node.setOwnerTree(null);
24794         // clear any references from the node
24795         node.parentNode = null;
24796         node.previousSibling = null;
24797         node.nextSibling = null;
24798         this.fireEvent("remove", this.ownerTree, this, node);
24799         return node;
24800     },
24801
24802     /**
24803      * Inserts the first node before the second node in this nodes childNodes collection.
24804      * @param {Node} node The node to insert
24805      * @param {Node} refNode The node to insert before (if null the node is appended)
24806      * @return {Node} The inserted node
24807      */
24808     insertBefore : function(node, refNode){
24809         if(!refNode){ // like standard Dom, refNode can be null for append
24810             return this.appendChild(node);
24811         }
24812         // nothing to do
24813         if(node == refNode){
24814             return false;
24815         }
24816
24817         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24818             return false;
24819         }
24820         var index = this.childNodes.indexOf(refNode);
24821         var oldParent = node.parentNode;
24822         var refIndex = index;
24823
24824         // when moving internally, indexes will change after remove
24825         if(oldParent == this && this.childNodes.indexOf(node) < index){
24826             refIndex--;
24827         }
24828
24829         // it's a move, make sure we move it cleanly
24830         if(oldParent){
24831             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24832                 return false;
24833             }
24834             oldParent.removeChild(node);
24835         }
24836         if(refIndex == 0){
24837             this.setFirstChild(node);
24838         }
24839         this.childNodes.splice(refIndex, 0, node);
24840         node.parentNode = this;
24841         var ps = this.childNodes[refIndex-1];
24842         if(ps){
24843             node.previousSibling = ps;
24844             ps.nextSibling = node;
24845         }else{
24846             node.previousSibling = null;
24847         }
24848         node.nextSibling = refNode;
24849         refNode.previousSibling = node;
24850         node.setOwnerTree(this.getOwnerTree());
24851         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24852         if(oldParent){
24853             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24854         }
24855         return node;
24856     },
24857
24858     /**
24859      * Returns the child node at the specified index.
24860      * @param {Number} index
24861      * @return {Node}
24862      */
24863     item : function(index){
24864         return this.childNodes[index];
24865     },
24866
24867     /**
24868      * Replaces one child node in this node with another.
24869      * @param {Node} newChild The replacement node
24870      * @param {Node} oldChild The node to replace
24871      * @return {Node} The replaced node
24872      */
24873     replaceChild : function(newChild, oldChild){
24874         this.insertBefore(newChild, oldChild);
24875         this.removeChild(oldChild);
24876         return oldChild;
24877     },
24878
24879     /**
24880      * Returns the index of a child node
24881      * @param {Node} node
24882      * @return {Number} The index of the node or -1 if it was not found
24883      */
24884     indexOf : function(child){
24885         return this.childNodes.indexOf(child);
24886     },
24887
24888     /**
24889      * Returns the tree this node is in.
24890      * @return {Tree}
24891      */
24892     getOwnerTree : function(){
24893         // if it doesn't have one, look for one
24894         if(!this.ownerTree){
24895             var p = this;
24896             while(p){
24897                 if(p.ownerTree){
24898                     this.ownerTree = p.ownerTree;
24899                     break;
24900                 }
24901                 p = p.parentNode;
24902             }
24903         }
24904         return this.ownerTree;
24905     },
24906
24907     /**
24908      * Returns depth of this node (the root node has a depth of 0)
24909      * @return {Number}
24910      */
24911     getDepth : function(){
24912         var depth = 0;
24913         var p = this;
24914         while(p.parentNode){
24915             ++depth;
24916             p = p.parentNode;
24917         }
24918         return depth;
24919     },
24920
24921     // private
24922     setOwnerTree : function(tree){
24923         // if it's move, we need to update everyone
24924         if(tree != this.ownerTree){
24925             if(this.ownerTree){
24926                 this.ownerTree.unregisterNode(this);
24927             }
24928             this.ownerTree = tree;
24929             var cs = this.childNodes;
24930             for(var i = 0, len = cs.length; i < len; i++) {
24931                 cs[i].setOwnerTree(tree);
24932             }
24933             if(tree){
24934                 tree.registerNode(this);
24935             }
24936         }
24937     },
24938
24939     /**
24940      * Returns the path for this node. The path can be used to expand or select this node programmatically.
24941      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
24942      * @return {String} The path
24943      */
24944     getPath : function(attr){
24945         attr = attr || "id";
24946         var p = this.parentNode;
24947         var b = [this.attributes[attr]];
24948         while(p){
24949             b.unshift(p.attributes[attr]);
24950             p = p.parentNode;
24951         }
24952         var sep = this.getOwnerTree().pathSeparator;
24953         return sep + b.join(sep);
24954     },
24955
24956     /**
24957      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
24958      * function call will be the scope provided or the current node. The arguments to the function
24959      * will be the args provided or the current node. If the function returns false at any point,
24960      * the bubble is stopped.
24961      * @param {Function} fn The function to call
24962      * @param {Object} scope (optional) The scope of the function (defaults to current node)
24963      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
24964      */
24965     bubble : function(fn, scope, args){
24966         var p = this;
24967         while(p){
24968             if(fn.call(scope || p, args || p) === false){
24969                 break;
24970             }
24971             p = p.parentNode;
24972         }
24973     },
24974
24975     /**
24976      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
24977      * function call will be the scope provided or the current node. The arguments to the function
24978      * will be the args provided or the current node. If the function returns false at any point,
24979      * the cascade is stopped on that branch.
24980      * @param {Function} fn The function to call
24981      * @param {Object} scope (optional) The scope of the function (defaults to current node)
24982      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
24983      */
24984     cascade : function(fn, scope, args){
24985         if(fn.call(scope || this, args || this) !== false){
24986             var cs = this.childNodes;
24987             for(var i = 0, len = cs.length; i < len; i++) {
24988                 cs[i].cascade(fn, scope, args);
24989             }
24990         }
24991     },
24992
24993     /**
24994      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
24995      * function call will be the scope provided or the current node. The arguments to the function
24996      * will be the args provided or the current node. If the function returns false at any point,
24997      * the iteration stops.
24998      * @param {Function} fn The function to call
24999      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25000      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25001      */
25002     eachChild : function(fn, scope, args){
25003         var cs = this.childNodes;
25004         for(var i = 0, len = cs.length; i < len; i++) {
25005                 if(fn.call(scope || this, args || cs[i]) === false){
25006                     break;
25007                 }
25008         }
25009     },
25010
25011     /**
25012      * Finds the first child that has the attribute with the specified value.
25013      * @param {String} attribute The attribute name
25014      * @param {Mixed} value The value to search for
25015      * @return {Node} The found child or null if none was found
25016      */
25017     findChild : function(attribute, value){
25018         var cs = this.childNodes;
25019         for(var i = 0, len = cs.length; i < len; i++) {
25020                 if(cs[i].attributes[attribute] == value){
25021                     return cs[i];
25022                 }
25023         }
25024         return null;
25025     },
25026
25027     /**
25028      * Finds the first child by a custom function. The child matches if the function passed
25029      * returns true.
25030      * @param {Function} fn
25031      * @param {Object} scope (optional)
25032      * @return {Node} The found child or null if none was found
25033      */
25034     findChildBy : function(fn, scope){
25035         var cs = this.childNodes;
25036         for(var i = 0, len = cs.length; i < len; i++) {
25037                 if(fn.call(scope||cs[i], cs[i]) === true){
25038                     return cs[i];
25039                 }
25040         }
25041         return null;
25042     },
25043
25044     /**
25045      * Sorts this nodes children using the supplied sort function
25046      * @param {Function} fn
25047      * @param {Object} scope (optional)
25048      */
25049     sort : function(fn, scope){
25050         var cs = this.childNodes;
25051         var len = cs.length;
25052         if(len > 0){
25053             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25054             cs.sort(sortFn);
25055             for(var i = 0; i < len; i++){
25056                 var n = cs[i];
25057                 n.previousSibling = cs[i-1];
25058                 n.nextSibling = cs[i+1];
25059                 if(i == 0){
25060                     this.setFirstChild(n);
25061                 }
25062                 if(i == len-1){
25063                     this.setLastChild(n);
25064                 }
25065             }
25066         }
25067     },
25068
25069     /**
25070      * Returns true if this node is an ancestor (at any point) of the passed node.
25071      * @param {Node} node
25072      * @return {Boolean}
25073      */
25074     contains : function(node){
25075         return node.isAncestor(this);
25076     },
25077
25078     /**
25079      * Returns true if the passed node is an ancestor (at any point) of this node.
25080      * @param {Node} node
25081      * @return {Boolean}
25082      */
25083     isAncestor : function(node){
25084         var p = this.parentNode;
25085         while(p){
25086             if(p == node){
25087                 return true;
25088             }
25089             p = p.parentNode;
25090         }
25091         return false;
25092     },
25093
25094     toString : function(){
25095         return "[Node"+(this.id?" "+this.id:"")+"]";
25096     }
25097 });/*
25098  * Based on:
25099  * Ext JS Library 1.1.1
25100  * Copyright(c) 2006-2007, Ext JS, LLC.
25101  *
25102  * Originally Released Under LGPL - original licence link has changed is not relivant.
25103  *
25104  * Fork - LGPL
25105  * <script type="text/javascript">
25106  */
25107  (function(){ 
25108 /**
25109  * @class Roo.Layer
25110  * @extends Roo.Element
25111  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25112  * automatic maintaining of shadow/shim positions.
25113  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25114  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25115  * you can pass a string with a CSS class name. False turns off the shadow.
25116  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25117  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25118  * @cfg {String} cls CSS class to add to the element
25119  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25120  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25121  * @constructor
25122  * @param {Object} config An object with config options.
25123  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25124  */
25125
25126 Roo.Layer = function(config, existingEl){
25127     config = config || {};
25128     var dh = Roo.DomHelper;
25129     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25130     if(existingEl){
25131         this.dom = Roo.getDom(existingEl);
25132     }
25133     if(!this.dom){
25134         var o = config.dh || {tag: "div", cls: "x-layer"};
25135         this.dom = dh.append(pel, o);
25136     }
25137     if(config.cls){
25138         this.addClass(config.cls);
25139     }
25140     this.constrain = config.constrain !== false;
25141     this.visibilityMode = Roo.Element.VISIBILITY;
25142     if(config.id){
25143         this.id = this.dom.id = config.id;
25144     }else{
25145         this.id = Roo.id(this.dom);
25146     }
25147     this.zindex = config.zindex || this.getZIndex();
25148     this.position("absolute", this.zindex);
25149     if(config.shadow){
25150         this.shadowOffset = config.shadowOffset || 4;
25151         this.shadow = new Roo.Shadow({
25152             offset : this.shadowOffset,
25153             mode : config.shadow
25154         });
25155     }else{
25156         this.shadowOffset = 0;
25157     }
25158     this.useShim = config.shim !== false && Roo.useShims;
25159     this.useDisplay = config.useDisplay;
25160     this.hide();
25161 };
25162
25163 var supr = Roo.Element.prototype;
25164
25165 // shims are shared among layer to keep from having 100 iframes
25166 var shims = [];
25167
25168 Roo.extend(Roo.Layer, Roo.Element, {
25169
25170     getZIndex : function(){
25171         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25172     },
25173
25174     getShim : function(){
25175         if(!this.useShim){
25176             return null;
25177         }
25178         if(this.shim){
25179             return this.shim;
25180         }
25181         var shim = shims.shift();
25182         if(!shim){
25183             shim = this.createShim();
25184             shim.enableDisplayMode('block');
25185             shim.dom.style.display = 'none';
25186             shim.dom.style.visibility = 'visible';
25187         }
25188         var pn = this.dom.parentNode;
25189         if(shim.dom.parentNode != pn){
25190             pn.insertBefore(shim.dom, this.dom);
25191         }
25192         shim.setStyle('z-index', this.getZIndex()-2);
25193         this.shim = shim;
25194         return shim;
25195     },
25196
25197     hideShim : function(){
25198         if(this.shim){
25199             this.shim.setDisplayed(false);
25200             shims.push(this.shim);
25201             delete this.shim;
25202         }
25203     },
25204
25205     disableShadow : function(){
25206         if(this.shadow){
25207             this.shadowDisabled = true;
25208             this.shadow.hide();
25209             this.lastShadowOffset = this.shadowOffset;
25210             this.shadowOffset = 0;
25211         }
25212     },
25213
25214     enableShadow : function(show){
25215         if(this.shadow){
25216             this.shadowDisabled = false;
25217             this.shadowOffset = this.lastShadowOffset;
25218             delete this.lastShadowOffset;
25219             if(show){
25220                 this.sync(true);
25221             }
25222         }
25223     },
25224
25225     // private
25226     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25227     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25228     sync : function(doShow){
25229         var sw = this.shadow;
25230         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25231             var sh = this.getShim();
25232
25233             var w = this.getWidth(),
25234                 h = this.getHeight();
25235
25236             var l = this.getLeft(true),
25237                 t = this.getTop(true);
25238
25239             if(sw && !this.shadowDisabled){
25240                 if(doShow && !sw.isVisible()){
25241                     sw.show(this);
25242                 }else{
25243                     sw.realign(l, t, w, h);
25244                 }
25245                 if(sh){
25246                     if(doShow){
25247                        sh.show();
25248                     }
25249                     // fit the shim behind the shadow, so it is shimmed too
25250                     var a = sw.adjusts, s = sh.dom.style;
25251                     s.left = (Math.min(l, l+a.l))+"px";
25252                     s.top = (Math.min(t, t+a.t))+"px";
25253                     s.width = (w+a.w)+"px";
25254                     s.height = (h+a.h)+"px";
25255                 }
25256             }else if(sh){
25257                 if(doShow){
25258                    sh.show();
25259                 }
25260                 sh.setSize(w, h);
25261                 sh.setLeftTop(l, t);
25262             }
25263             
25264         }
25265     },
25266
25267     // private
25268     destroy : function(){
25269         this.hideShim();
25270         if(this.shadow){
25271             this.shadow.hide();
25272         }
25273         this.removeAllListeners();
25274         var pn = this.dom.parentNode;
25275         if(pn){
25276             pn.removeChild(this.dom);
25277         }
25278         Roo.Element.uncache(this.id);
25279     },
25280
25281     remove : function(){
25282         this.destroy();
25283     },
25284
25285     // private
25286     beginUpdate : function(){
25287         this.updating = true;
25288     },
25289
25290     // private
25291     endUpdate : function(){
25292         this.updating = false;
25293         this.sync(true);
25294     },
25295
25296     // private
25297     hideUnders : function(negOffset){
25298         if(this.shadow){
25299             this.shadow.hide();
25300         }
25301         this.hideShim();
25302     },
25303
25304     // private
25305     constrainXY : function(){
25306         if(this.constrain){
25307             var vw = Roo.lib.Dom.getViewWidth(),
25308                 vh = Roo.lib.Dom.getViewHeight();
25309             var s = Roo.get(document).getScroll();
25310
25311             var xy = this.getXY();
25312             var x = xy[0], y = xy[1];   
25313             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25314             // only move it if it needs it
25315             var moved = false;
25316             // first validate right/bottom
25317             if((x + w) > vw+s.left){
25318                 x = vw - w - this.shadowOffset;
25319                 moved = true;
25320             }
25321             if((y + h) > vh+s.top){
25322                 y = vh - h - this.shadowOffset;
25323                 moved = true;
25324             }
25325             // then make sure top/left isn't negative
25326             if(x < s.left){
25327                 x = s.left;
25328                 moved = true;
25329             }
25330             if(y < s.top){
25331                 y = s.top;
25332                 moved = true;
25333             }
25334             if(moved){
25335                 if(this.avoidY){
25336                     var ay = this.avoidY;
25337                     if(y <= ay && (y+h) >= ay){
25338                         y = ay-h-5;   
25339                     }
25340                 }
25341                 xy = [x, y];
25342                 this.storeXY(xy);
25343                 supr.setXY.call(this, xy);
25344                 this.sync();
25345             }
25346         }
25347     },
25348
25349     isVisible : function(){
25350         return this.visible;    
25351     },
25352
25353     // private
25354     showAction : function(){
25355         this.visible = true; // track visibility to prevent getStyle calls
25356         if(this.useDisplay === true){
25357             this.setDisplayed("");
25358         }else if(this.lastXY){
25359             supr.setXY.call(this, this.lastXY);
25360         }else if(this.lastLT){
25361             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25362         }
25363     },
25364
25365     // private
25366     hideAction : function(){
25367         this.visible = false;
25368         if(this.useDisplay === true){
25369             this.setDisplayed(false);
25370         }else{
25371             this.setLeftTop(-10000,-10000);
25372         }
25373     },
25374
25375     // overridden Element method
25376     setVisible : function(v, a, d, c, e){
25377         if(v){
25378             this.showAction();
25379         }
25380         if(a && v){
25381             var cb = function(){
25382                 this.sync(true);
25383                 if(c){
25384                     c();
25385                 }
25386             }.createDelegate(this);
25387             supr.setVisible.call(this, true, true, d, cb, e);
25388         }else{
25389             if(!v){
25390                 this.hideUnders(true);
25391             }
25392             var cb = c;
25393             if(a){
25394                 cb = function(){
25395                     this.hideAction();
25396                     if(c){
25397                         c();
25398                     }
25399                 }.createDelegate(this);
25400             }
25401             supr.setVisible.call(this, v, a, d, cb, e);
25402             if(v){
25403                 this.sync(true);
25404             }else if(!a){
25405                 this.hideAction();
25406             }
25407         }
25408     },
25409
25410     storeXY : function(xy){
25411         delete this.lastLT;
25412         this.lastXY = xy;
25413     },
25414
25415     storeLeftTop : function(left, top){
25416         delete this.lastXY;
25417         this.lastLT = [left, top];
25418     },
25419
25420     // private
25421     beforeFx : function(){
25422         this.beforeAction();
25423         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25424     },
25425
25426     // private
25427     afterFx : function(){
25428         Roo.Layer.superclass.afterFx.apply(this, arguments);
25429         this.sync(this.isVisible());
25430     },
25431
25432     // private
25433     beforeAction : function(){
25434         if(!this.updating && this.shadow){
25435             this.shadow.hide();
25436         }
25437     },
25438
25439     // overridden Element method
25440     setLeft : function(left){
25441         this.storeLeftTop(left, this.getTop(true));
25442         supr.setLeft.apply(this, arguments);
25443         this.sync();
25444     },
25445
25446     setTop : function(top){
25447         this.storeLeftTop(this.getLeft(true), top);
25448         supr.setTop.apply(this, arguments);
25449         this.sync();
25450     },
25451
25452     setLeftTop : function(left, top){
25453         this.storeLeftTop(left, top);
25454         supr.setLeftTop.apply(this, arguments);
25455         this.sync();
25456     },
25457
25458     setXY : function(xy, a, d, c, e){
25459         this.fixDisplay();
25460         this.beforeAction();
25461         this.storeXY(xy);
25462         var cb = this.createCB(c);
25463         supr.setXY.call(this, xy, a, d, cb, e);
25464         if(!a){
25465             cb();
25466         }
25467     },
25468
25469     // private
25470     createCB : function(c){
25471         var el = this;
25472         return function(){
25473             el.constrainXY();
25474             el.sync(true);
25475             if(c){
25476                 c();
25477             }
25478         };
25479     },
25480
25481     // overridden Element method
25482     setX : function(x, a, d, c, e){
25483         this.setXY([x, this.getY()], a, d, c, e);
25484     },
25485
25486     // overridden Element method
25487     setY : function(y, a, d, c, e){
25488         this.setXY([this.getX(), y], a, d, c, e);
25489     },
25490
25491     // overridden Element method
25492     setSize : function(w, h, a, d, c, e){
25493         this.beforeAction();
25494         var cb = this.createCB(c);
25495         supr.setSize.call(this, w, h, a, d, cb, e);
25496         if(!a){
25497             cb();
25498         }
25499     },
25500
25501     // overridden Element method
25502     setWidth : function(w, a, d, c, e){
25503         this.beforeAction();
25504         var cb = this.createCB(c);
25505         supr.setWidth.call(this, w, a, d, cb, e);
25506         if(!a){
25507             cb();
25508         }
25509     },
25510
25511     // overridden Element method
25512     setHeight : function(h, a, d, c, e){
25513         this.beforeAction();
25514         var cb = this.createCB(c);
25515         supr.setHeight.call(this, h, a, d, cb, e);
25516         if(!a){
25517             cb();
25518         }
25519     },
25520
25521     // overridden Element method
25522     setBounds : function(x, y, w, h, a, d, c, e){
25523         this.beforeAction();
25524         var cb = this.createCB(c);
25525         if(!a){
25526             this.storeXY([x, y]);
25527             supr.setXY.call(this, [x, y]);
25528             supr.setSize.call(this, w, h, a, d, cb, e);
25529             cb();
25530         }else{
25531             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25532         }
25533         return this;
25534     },
25535     
25536     /**
25537      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25538      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25539      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25540      * @param {Number} zindex The new z-index to set
25541      * @return {this} The Layer
25542      */
25543     setZIndex : function(zindex){
25544         this.zindex = zindex;
25545         this.setStyle("z-index", zindex + 2);
25546         if(this.shadow){
25547             this.shadow.setZIndex(zindex + 1);
25548         }
25549         if(this.shim){
25550             this.shim.setStyle("z-index", zindex);
25551         }
25552     }
25553 });
25554 })();/*
25555  * Based on:
25556  * Ext JS Library 1.1.1
25557  * Copyright(c) 2006-2007, Ext JS, LLC.
25558  *
25559  * Originally Released Under LGPL - original licence link has changed is not relivant.
25560  *
25561  * Fork - LGPL
25562  * <script type="text/javascript">
25563  */
25564
25565
25566 /**
25567  * @class Roo.Shadow
25568  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25569  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25570  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25571  * @constructor
25572  * Create a new Shadow
25573  * @param {Object} config The config object
25574  */
25575 Roo.Shadow = function(config){
25576     Roo.apply(this, config);
25577     if(typeof this.mode != "string"){
25578         this.mode = this.defaultMode;
25579     }
25580     var o = this.offset, a = {h: 0};
25581     var rad = Math.floor(this.offset/2);
25582     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25583         case "drop":
25584             a.w = 0;
25585             a.l = a.t = o;
25586             a.t -= 1;
25587             if(Roo.isIE){
25588                 a.l -= this.offset + rad;
25589                 a.t -= this.offset + rad;
25590                 a.w -= rad;
25591                 a.h -= rad;
25592                 a.t += 1;
25593             }
25594         break;
25595         case "sides":
25596             a.w = (o*2);
25597             a.l = -o;
25598             a.t = o-1;
25599             if(Roo.isIE){
25600                 a.l -= (this.offset - rad);
25601                 a.t -= this.offset + rad;
25602                 a.l += 1;
25603                 a.w -= (this.offset - rad)*2;
25604                 a.w -= rad + 1;
25605                 a.h -= 1;
25606             }
25607         break;
25608         case "frame":
25609             a.w = a.h = (o*2);
25610             a.l = a.t = -o;
25611             a.t += 1;
25612             a.h -= 2;
25613             if(Roo.isIE){
25614                 a.l -= (this.offset - rad);
25615                 a.t -= (this.offset - rad);
25616                 a.l += 1;
25617                 a.w -= (this.offset + rad + 1);
25618                 a.h -= (this.offset + rad);
25619                 a.h += 1;
25620             }
25621         break;
25622     };
25623
25624     this.adjusts = a;
25625 };
25626
25627 Roo.Shadow.prototype = {
25628     /**
25629      * @cfg {String} mode
25630      * The shadow display mode.  Supports the following options:<br />
25631      * sides: Shadow displays on both sides and bottom only<br />
25632      * frame: Shadow displays equally on all four sides<br />
25633      * drop: Traditional bottom-right drop shadow (default)
25634      */
25635     /**
25636      * @cfg {String} offset
25637      * The number of pixels to offset the shadow from the element (defaults to 4)
25638      */
25639     offset: 4,
25640
25641     // private
25642     defaultMode: "drop",
25643
25644     /**
25645      * Displays the shadow under the target element
25646      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25647      */
25648     show : function(target){
25649         target = Roo.get(target);
25650         if(!this.el){
25651             this.el = Roo.Shadow.Pool.pull();
25652             if(this.el.dom.nextSibling != target.dom){
25653                 this.el.insertBefore(target);
25654             }
25655         }
25656         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25657         if(Roo.isIE){
25658             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25659         }
25660         this.realign(
25661             target.getLeft(true),
25662             target.getTop(true),
25663             target.getWidth(),
25664             target.getHeight()
25665         );
25666         this.el.dom.style.display = "block";
25667     },
25668
25669     /**
25670      * Returns true if the shadow is visible, else false
25671      */
25672     isVisible : function(){
25673         return this.el ? true : false;  
25674     },
25675
25676     /**
25677      * Direct alignment when values are already available. Show must be called at least once before
25678      * calling this method to ensure it is initialized.
25679      * @param {Number} left The target element left position
25680      * @param {Number} top The target element top position
25681      * @param {Number} width The target element width
25682      * @param {Number} height The target element height
25683      */
25684     realign : function(l, t, w, h){
25685         if(!this.el){
25686             return;
25687         }
25688         var a = this.adjusts, d = this.el.dom, s = d.style;
25689         var iea = 0;
25690         s.left = (l+a.l)+"px";
25691         s.top = (t+a.t)+"px";
25692         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25693  
25694         if(s.width != sws || s.height != shs){
25695             s.width = sws;
25696             s.height = shs;
25697             if(!Roo.isIE){
25698                 var cn = d.childNodes;
25699                 var sww = Math.max(0, (sw-12))+"px";
25700                 cn[0].childNodes[1].style.width = sww;
25701                 cn[1].childNodes[1].style.width = sww;
25702                 cn[2].childNodes[1].style.width = sww;
25703                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25704             }
25705         }
25706     },
25707
25708     /**
25709      * Hides this shadow
25710      */
25711     hide : function(){
25712         if(this.el){
25713             this.el.dom.style.display = "none";
25714             Roo.Shadow.Pool.push(this.el);
25715             delete this.el;
25716         }
25717     },
25718
25719     /**
25720      * Adjust the z-index of this shadow
25721      * @param {Number} zindex The new z-index
25722      */
25723     setZIndex : function(z){
25724         this.zIndex = z;
25725         if(this.el){
25726             this.el.setStyle("z-index", z);
25727         }
25728     }
25729 };
25730
25731 // Private utility class that manages the internal Shadow cache
25732 Roo.Shadow.Pool = function(){
25733     var p = [];
25734     var markup = Roo.isIE ?
25735                  '<div class="x-ie-shadow"></div>' :
25736                  '<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>';
25737     return {
25738         pull : function(){
25739             var sh = p.shift();
25740             if(!sh){
25741                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25742                 sh.autoBoxAdjust = false;
25743             }
25744             return sh;
25745         },
25746
25747         push : function(sh){
25748             p.push(sh);
25749         }
25750     };
25751 }();/*
25752  * Based on:
25753  * Ext JS Library 1.1.1
25754  * Copyright(c) 2006-2007, Ext JS, LLC.
25755  *
25756  * Originally Released Under LGPL - original licence link has changed is not relivant.
25757  *
25758  * Fork - LGPL
25759  * <script type="text/javascript">
25760  */
25761
25762
25763 /**
25764  * @class Roo.SplitBar
25765  * @extends Roo.util.Observable
25766  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25767  * <br><br>
25768  * Usage:
25769  * <pre><code>
25770 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25771                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25772 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25773 split.minSize = 100;
25774 split.maxSize = 600;
25775 split.animate = true;
25776 split.on('moved', splitterMoved);
25777 </code></pre>
25778  * @constructor
25779  * Create a new SplitBar
25780  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25781  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25782  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25783  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25784                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25785                         position of the SplitBar).
25786  */
25787 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25788     
25789     /** @private */
25790     this.el = Roo.get(dragElement, true);
25791     this.el.dom.unselectable = "on";
25792     /** @private */
25793     this.resizingEl = Roo.get(resizingElement, true);
25794
25795     /**
25796      * @private
25797      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25798      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25799      * @type Number
25800      */
25801     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25802     
25803     /**
25804      * The minimum size of the resizing element. (Defaults to 0)
25805      * @type Number
25806      */
25807     this.minSize = 0;
25808     
25809     /**
25810      * The maximum size of the resizing element. (Defaults to 2000)
25811      * @type Number
25812      */
25813     this.maxSize = 2000;
25814     
25815     /**
25816      * Whether to animate the transition to the new size
25817      * @type Boolean
25818      */
25819     this.animate = false;
25820     
25821     /**
25822      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25823      * @type Boolean
25824      */
25825     this.useShim = false;
25826     
25827     /** @private */
25828     this.shim = null;
25829     
25830     if(!existingProxy){
25831         /** @private */
25832         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25833     }else{
25834         this.proxy = Roo.get(existingProxy).dom;
25835     }
25836     /** @private */
25837     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25838     
25839     /** @private */
25840     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25841     
25842     /** @private */
25843     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25844     
25845     /** @private */
25846     this.dragSpecs = {};
25847     
25848     /**
25849      * @private The adapter to use to positon and resize elements
25850      */
25851     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25852     this.adapter.init(this);
25853     
25854     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25855         /** @private */
25856         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25857         this.el.addClass("x-splitbar-h");
25858     }else{
25859         /** @private */
25860         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25861         this.el.addClass("x-splitbar-v");
25862     }
25863     
25864     this.addEvents({
25865         /**
25866          * @event resize
25867          * Fires when the splitter is moved (alias for {@link #event-moved})
25868          * @param {Roo.SplitBar} this
25869          * @param {Number} newSize the new width or height
25870          */
25871         "resize" : true,
25872         /**
25873          * @event moved
25874          * Fires when the splitter is moved
25875          * @param {Roo.SplitBar} this
25876          * @param {Number} newSize the new width or height
25877          */
25878         "moved" : true,
25879         /**
25880          * @event beforeresize
25881          * Fires before the splitter is dragged
25882          * @param {Roo.SplitBar} this
25883          */
25884         "beforeresize" : true,
25885
25886         "beforeapply" : true
25887     });
25888
25889     Roo.util.Observable.call(this);
25890 };
25891
25892 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
25893     onStartProxyDrag : function(x, y){
25894         this.fireEvent("beforeresize", this);
25895         if(!this.overlay){
25896             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
25897             o.unselectable();
25898             o.enableDisplayMode("block");
25899             // all splitbars share the same overlay
25900             Roo.SplitBar.prototype.overlay = o;
25901         }
25902         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
25903         this.overlay.show();
25904         Roo.get(this.proxy).setDisplayed("block");
25905         var size = this.adapter.getElementSize(this);
25906         this.activeMinSize = this.getMinimumSize();;
25907         this.activeMaxSize = this.getMaximumSize();;
25908         var c1 = size - this.activeMinSize;
25909         var c2 = Math.max(this.activeMaxSize - size, 0);
25910         if(this.orientation == Roo.SplitBar.HORIZONTAL){
25911             this.dd.resetConstraints();
25912             this.dd.setXConstraint(
25913                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
25914                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
25915             );
25916             this.dd.setYConstraint(0, 0);
25917         }else{
25918             this.dd.resetConstraints();
25919             this.dd.setXConstraint(0, 0);
25920             this.dd.setYConstraint(
25921                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
25922                 this.placement == Roo.SplitBar.TOP ? c2 : c1
25923             );
25924          }
25925         this.dragSpecs.startSize = size;
25926         this.dragSpecs.startPoint = [x, y];
25927         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
25928     },
25929     
25930     /** 
25931      * @private Called after the drag operation by the DDProxy
25932      */
25933     onEndProxyDrag : function(e){
25934         Roo.get(this.proxy).setDisplayed(false);
25935         var endPoint = Roo.lib.Event.getXY(e);
25936         if(this.overlay){
25937             this.overlay.hide();
25938         }
25939         var newSize;
25940         if(this.orientation == Roo.SplitBar.HORIZONTAL){
25941             newSize = this.dragSpecs.startSize + 
25942                 (this.placement == Roo.SplitBar.LEFT ?
25943                     endPoint[0] - this.dragSpecs.startPoint[0] :
25944                     this.dragSpecs.startPoint[0] - endPoint[0]
25945                 );
25946         }else{
25947             newSize = this.dragSpecs.startSize + 
25948                 (this.placement == Roo.SplitBar.TOP ?
25949                     endPoint[1] - this.dragSpecs.startPoint[1] :
25950                     this.dragSpecs.startPoint[1] - endPoint[1]
25951                 );
25952         }
25953         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
25954         if(newSize != this.dragSpecs.startSize){
25955             if(this.fireEvent('beforeapply', this, newSize) !== false){
25956                 this.adapter.setElementSize(this, newSize);
25957                 this.fireEvent("moved", this, newSize);
25958                 this.fireEvent("resize", this, newSize);
25959             }
25960         }
25961     },
25962     
25963     /**
25964      * Get the adapter this SplitBar uses
25965      * @return The adapter object
25966      */
25967     getAdapter : function(){
25968         return this.adapter;
25969     },
25970     
25971     /**
25972      * Set the adapter this SplitBar uses
25973      * @param {Object} adapter A SplitBar adapter object
25974      */
25975     setAdapter : function(adapter){
25976         this.adapter = adapter;
25977         this.adapter.init(this);
25978     },
25979     
25980     /**
25981      * Gets the minimum size for the resizing element
25982      * @return {Number} The minimum size
25983      */
25984     getMinimumSize : function(){
25985         return this.minSize;
25986     },
25987     
25988     /**
25989      * Sets the minimum size for the resizing element
25990      * @param {Number} minSize The minimum size
25991      */
25992     setMinimumSize : function(minSize){
25993         this.minSize = minSize;
25994     },
25995     
25996     /**
25997      * Gets the maximum size for the resizing element
25998      * @return {Number} The maximum size
25999      */
26000     getMaximumSize : function(){
26001         return this.maxSize;
26002     },
26003     
26004     /**
26005      * Sets the maximum size for the resizing element
26006      * @param {Number} maxSize The maximum size
26007      */
26008     setMaximumSize : function(maxSize){
26009         this.maxSize = maxSize;
26010     },
26011     
26012     /**
26013      * Sets the initialize size for the resizing element
26014      * @param {Number} size The initial size
26015      */
26016     setCurrentSize : function(size){
26017         var oldAnimate = this.animate;
26018         this.animate = false;
26019         this.adapter.setElementSize(this, size);
26020         this.animate = oldAnimate;
26021     },
26022     
26023     /**
26024      * Destroy this splitbar. 
26025      * @param {Boolean} removeEl True to remove the element
26026      */
26027     destroy : function(removeEl){
26028         if(this.shim){
26029             this.shim.remove();
26030         }
26031         this.dd.unreg();
26032         this.proxy.parentNode.removeChild(this.proxy);
26033         if(removeEl){
26034             this.el.remove();
26035         }
26036     }
26037 });
26038
26039 /**
26040  * @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.
26041  */
26042 Roo.SplitBar.createProxy = function(dir){
26043     var proxy = new Roo.Element(document.createElement("div"));
26044     proxy.unselectable();
26045     var cls = 'x-splitbar-proxy';
26046     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26047     document.body.appendChild(proxy.dom);
26048     return proxy.dom;
26049 };
26050
26051 /** 
26052  * @class Roo.SplitBar.BasicLayoutAdapter
26053  * Default Adapter. It assumes the splitter and resizing element are not positioned
26054  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26055  */
26056 Roo.SplitBar.BasicLayoutAdapter = function(){
26057 };
26058
26059 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26060     // do nothing for now
26061     init : function(s){
26062     
26063     },
26064     /**
26065      * Called before drag operations to get the current size of the resizing element. 
26066      * @param {Roo.SplitBar} s The SplitBar using this adapter
26067      */
26068      getElementSize : function(s){
26069         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26070             return s.resizingEl.getWidth();
26071         }else{
26072             return s.resizingEl.getHeight();
26073         }
26074     },
26075     
26076     /**
26077      * Called after drag operations to set the size of the resizing element.
26078      * @param {Roo.SplitBar} s The SplitBar using this adapter
26079      * @param {Number} newSize The new size to set
26080      * @param {Function} onComplete A function to be invoked when resizing is complete
26081      */
26082     setElementSize : function(s, newSize, onComplete){
26083         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26084             if(!s.animate){
26085                 s.resizingEl.setWidth(newSize);
26086                 if(onComplete){
26087                     onComplete(s, newSize);
26088                 }
26089             }else{
26090                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26091             }
26092         }else{
26093             
26094             if(!s.animate){
26095                 s.resizingEl.setHeight(newSize);
26096                 if(onComplete){
26097                     onComplete(s, newSize);
26098                 }
26099             }else{
26100                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26101             }
26102         }
26103     }
26104 };
26105
26106 /** 
26107  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26108  * @extends Roo.SplitBar.BasicLayoutAdapter
26109  * Adapter that  moves the splitter element to align with the resized sizing element. 
26110  * Used with an absolute positioned SplitBar.
26111  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26112  * document.body, make sure you assign an id to the body element.
26113  */
26114 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26115     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26116     this.container = Roo.get(container);
26117 };
26118
26119 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26120     init : function(s){
26121         this.basic.init(s);
26122     },
26123     
26124     getElementSize : function(s){
26125         return this.basic.getElementSize(s);
26126     },
26127     
26128     setElementSize : function(s, newSize, onComplete){
26129         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26130     },
26131     
26132     moveSplitter : function(s){
26133         var yes = Roo.SplitBar;
26134         switch(s.placement){
26135             case yes.LEFT:
26136                 s.el.setX(s.resizingEl.getRight());
26137                 break;
26138             case yes.RIGHT:
26139                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26140                 break;
26141             case yes.TOP:
26142                 s.el.setY(s.resizingEl.getBottom());
26143                 break;
26144             case yes.BOTTOM:
26145                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26146                 break;
26147         }
26148     }
26149 };
26150
26151 /**
26152  * Orientation constant - Create a vertical SplitBar
26153  * @static
26154  * @type Number
26155  */
26156 Roo.SplitBar.VERTICAL = 1;
26157
26158 /**
26159  * Orientation constant - Create a horizontal SplitBar
26160  * @static
26161  * @type Number
26162  */
26163 Roo.SplitBar.HORIZONTAL = 2;
26164
26165 /**
26166  * Placement constant - The resizing element is to the left of the splitter element
26167  * @static
26168  * @type Number
26169  */
26170 Roo.SplitBar.LEFT = 1;
26171
26172 /**
26173  * Placement constant - The resizing element is to the right of the splitter element
26174  * @static
26175  * @type Number
26176  */
26177 Roo.SplitBar.RIGHT = 2;
26178
26179 /**
26180  * Placement constant - The resizing element is positioned above the splitter element
26181  * @static
26182  * @type Number
26183  */
26184 Roo.SplitBar.TOP = 3;
26185
26186 /**
26187  * Placement constant - The resizing element is positioned under splitter element
26188  * @static
26189  * @type Number
26190  */
26191 Roo.SplitBar.BOTTOM = 4;
26192 /*
26193  * Based on:
26194  * Ext JS Library 1.1.1
26195  * Copyright(c) 2006-2007, Ext JS, LLC.
26196  *
26197  * Originally Released Under LGPL - original licence link has changed is not relivant.
26198  *
26199  * Fork - LGPL
26200  * <script type="text/javascript">
26201  */
26202
26203 /**
26204  * @class Roo.View
26205  * @extends Roo.util.Observable
26206  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26207  * This class also supports single and multi selection modes. <br>
26208  * Create a data model bound view:
26209  <pre><code>
26210  var store = new Roo.data.Store(...);
26211
26212  var view = new Roo.View({
26213     el : "my-element",
26214     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26215  
26216     singleSelect: true,
26217     selectedClass: "ydataview-selected",
26218     store: store
26219  });
26220
26221  // listen for node click?
26222  view.on("click", function(vw, index, node, e){
26223  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26224  });
26225
26226  // load XML data
26227  dataModel.load("foobar.xml");
26228  </code></pre>
26229  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26230  * <br><br>
26231  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26232  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26233  * 
26234  * Note: old style constructor is still suported (container, template, config)
26235  * 
26236  * @constructor
26237  * Create a new View
26238  * @param {Object} config The config object
26239  * 
26240  */
26241 Roo.View = function(config, depreciated_tpl, depreciated_config){
26242     
26243     this.parent = false;
26244     
26245     if (typeof(depreciated_tpl) == 'undefined') {
26246         // new way.. - universal constructor.
26247         Roo.apply(this, config);
26248         this.el  = Roo.get(this.el);
26249     } else {
26250         // old format..
26251         this.el  = Roo.get(config);
26252         this.tpl = depreciated_tpl;
26253         Roo.apply(this, depreciated_config);
26254     }
26255     this.wrapEl  = this.el.wrap().wrap();
26256     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26257     
26258     
26259     if(typeof(this.tpl) == "string"){
26260         this.tpl = new Roo.Template(this.tpl);
26261     } else {
26262         // support xtype ctors..
26263         this.tpl = new Roo.factory(this.tpl, Roo);
26264     }
26265     
26266     
26267     this.tpl.compile();
26268     
26269     /** @private */
26270     this.addEvents({
26271         /**
26272          * @event beforeclick
26273          * Fires before a click is processed. Returns false to cancel the default action.
26274          * @param {Roo.View} this
26275          * @param {Number} index The index of the target node
26276          * @param {HTMLElement} node The target node
26277          * @param {Roo.EventObject} e The raw event object
26278          */
26279             "beforeclick" : true,
26280         /**
26281          * @event click
26282          * Fires when a template node is clicked.
26283          * @param {Roo.View} this
26284          * @param {Number} index The index of the target node
26285          * @param {HTMLElement} node The target node
26286          * @param {Roo.EventObject} e The raw event object
26287          */
26288             "click" : true,
26289         /**
26290          * @event dblclick
26291          * Fires when a template node is double clicked.
26292          * @param {Roo.View} this
26293          * @param {Number} index The index of the target node
26294          * @param {HTMLElement} node The target node
26295          * @param {Roo.EventObject} e The raw event object
26296          */
26297             "dblclick" : true,
26298         /**
26299          * @event contextmenu
26300          * Fires when a template node is right clicked.
26301          * @param {Roo.View} this
26302          * @param {Number} index The index of the target node
26303          * @param {HTMLElement} node The target node
26304          * @param {Roo.EventObject} e The raw event object
26305          */
26306             "contextmenu" : true,
26307         /**
26308          * @event selectionchange
26309          * Fires when the selected nodes change.
26310          * @param {Roo.View} this
26311          * @param {Array} selections Array of the selected nodes
26312          */
26313             "selectionchange" : true,
26314     
26315         /**
26316          * @event beforeselect
26317          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26318          * @param {Roo.View} this
26319          * @param {HTMLElement} node The node to be selected
26320          * @param {Array} selections Array of currently selected nodes
26321          */
26322             "beforeselect" : true,
26323         /**
26324          * @event preparedata
26325          * Fires on every row to render, to allow you to change the data.
26326          * @param {Roo.View} this
26327          * @param {Object} data to be rendered (change this)
26328          */
26329           "preparedata" : true
26330           
26331           
26332         });
26333
26334
26335
26336     this.el.on({
26337         "click": this.onClick,
26338         "dblclick": this.onDblClick,
26339         "contextmenu": this.onContextMenu,
26340         scope:this
26341     });
26342
26343     this.selections = [];
26344     this.nodes = [];
26345     this.cmp = new Roo.CompositeElementLite([]);
26346     if(this.store){
26347         this.store = Roo.factory(this.store, Roo.data);
26348         this.setStore(this.store, true);
26349     }
26350     
26351     if ( this.footer && this.footer.xtype) {
26352            
26353          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26354         
26355         this.footer.dataSource = this.store;
26356         this.footer.container = fctr;
26357         this.footer = Roo.factory(this.footer, Roo);
26358         fctr.insertFirst(this.el);
26359         
26360         // this is a bit insane - as the paging toolbar seems to detach the el..
26361 //        dom.parentNode.parentNode.parentNode
26362          // they get detached?
26363     }
26364     
26365     
26366     Roo.View.superclass.constructor.call(this);
26367     
26368     
26369 };
26370
26371 Roo.extend(Roo.View, Roo.util.Observable, {
26372     
26373      /**
26374      * @cfg {Roo.data.Store} store Data store to load data from.
26375      */
26376     store : false,
26377     
26378     /**
26379      * @cfg {String|Roo.Element} el The container element.
26380      */
26381     el : '',
26382     
26383     /**
26384      * @cfg {String|Roo.Template} tpl The template used by this View 
26385      */
26386     tpl : false,
26387     /**
26388      * @cfg {String} dataName the named area of the template to use as the data area
26389      *                          Works with domtemplates roo-name="name"
26390      */
26391     dataName: false,
26392     /**
26393      * @cfg {String} selectedClass The css class to add to selected nodes
26394      */
26395     selectedClass : "x-view-selected",
26396      /**
26397      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26398      */
26399     emptyText : "",
26400     
26401     /**
26402      * @cfg {String} text to display on mask (default Loading)
26403      */
26404     mask : false,
26405     /**
26406      * @cfg {Boolean} multiSelect Allow multiple selection
26407      */
26408     multiSelect : false,
26409     /**
26410      * @cfg {Boolean} singleSelect Allow single selection
26411      */
26412     singleSelect:  false,
26413     
26414     /**
26415      * @cfg {Boolean} toggleSelect - selecting 
26416      */
26417     toggleSelect : false,
26418     
26419     /**
26420      * @cfg {Boolean} tickable - selecting 
26421      */
26422     tickable : false,
26423     
26424     /**
26425      * Returns the element this view is bound to.
26426      * @return {Roo.Element}
26427      */
26428     getEl : function(){
26429         return this.wrapEl;
26430     },
26431     
26432     
26433
26434     /**
26435      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26436      */
26437     refresh : function(){
26438         //Roo.log('refresh');
26439         var t = this.tpl;
26440         
26441         // if we are using something like 'domtemplate', then
26442         // the what gets used is:
26443         // t.applySubtemplate(NAME, data, wrapping data..)
26444         // the outer template then get' applied with
26445         //     the store 'extra data'
26446         // and the body get's added to the
26447         //      roo-name="data" node?
26448         //      <span class='roo-tpl-{name}'></span> ?????
26449         
26450         
26451         
26452         this.clearSelections();
26453         this.el.update("");
26454         var html = [];
26455         var records = this.store.getRange();
26456         if(records.length < 1) {
26457             
26458             // is this valid??  = should it render a template??
26459             
26460             this.el.update(this.emptyText);
26461             return;
26462         }
26463         var el = this.el;
26464         if (this.dataName) {
26465             this.el.update(t.apply(this.store.meta)); //????
26466             el = this.el.child('.roo-tpl-' + this.dataName);
26467         }
26468         
26469         for(var i = 0, len = records.length; i < len; i++){
26470             var data = this.prepareData(records[i].data, i, records[i]);
26471             this.fireEvent("preparedata", this, data, i, records[i]);
26472             
26473             var d = Roo.apply({}, data);
26474             
26475             if(this.tickable){
26476                 Roo.apply(d, {'roo-id' : Roo.id()});
26477                 
26478                 var _this = this;
26479             
26480                 Roo.each(this.parent.item, function(item){
26481                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26482                         return;
26483                     }
26484                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26485                 });
26486             }
26487             
26488             html[html.length] = Roo.util.Format.trim(
26489                 this.dataName ?
26490                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26491                     t.apply(d)
26492             );
26493         }
26494         
26495         
26496         
26497         el.update(html.join(""));
26498         this.nodes = el.dom.childNodes;
26499         this.updateIndexes(0);
26500     },
26501     
26502
26503     /**
26504      * Function to override to reformat the data that is sent to
26505      * the template for each node.
26506      * DEPRICATED - use the preparedata event handler.
26507      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26508      * a JSON object for an UpdateManager bound view).
26509      */
26510     prepareData : function(data, index, record)
26511     {
26512         this.fireEvent("preparedata", this, data, index, record);
26513         return data;
26514     },
26515
26516     onUpdate : function(ds, record){
26517         // Roo.log('on update');   
26518         this.clearSelections();
26519         var index = this.store.indexOf(record);
26520         var n = this.nodes[index];
26521         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26522         n.parentNode.removeChild(n);
26523         this.updateIndexes(index, index);
26524     },
26525
26526     
26527     
26528 // --------- FIXME     
26529     onAdd : function(ds, records, index)
26530     {
26531         //Roo.log(['on Add', ds, records, index] );        
26532         this.clearSelections();
26533         if(this.nodes.length == 0){
26534             this.refresh();
26535             return;
26536         }
26537         var n = this.nodes[index];
26538         for(var i = 0, len = records.length; i < len; i++){
26539             var d = this.prepareData(records[i].data, i, records[i]);
26540             if(n){
26541                 this.tpl.insertBefore(n, d);
26542             }else{
26543                 
26544                 this.tpl.append(this.el, d);
26545             }
26546         }
26547         this.updateIndexes(index);
26548     },
26549
26550     onRemove : function(ds, record, index){
26551        // Roo.log('onRemove');
26552         this.clearSelections();
26553         var el = this.dataName  ?
26554             this.el.child('.roo-tpl-' + this.dataName) :
26555             this.el; 
26556         
26557         el.dom.removeChild(this.nodes[index]);
26558         this.updateIndexes(index);
26559     },
26560
26561     /**
26562      * Refresh an individual node.
26563      * @param {Number} index
26564      */
26565     refreshNode : function(index){
26566         this.onUpdate(this.store, this.store.getAt(index));
26567     },
26568
26569     updateIndexes : function(startIndex, endIndex){
26570         var ns = this.nodes;
26571         startIndex = startIndex || 0;
26572         endIndex = endIndex || ns.length - 1;
26573         for(var i = startIndex; i <= endIndex; i++){
26574             ns[i].nodeIndex = i;
26575         }
26576     },
26577
26578     /**
26579      * Changes the data store this view uses and refresh the view.
26580      * @param {Store} store
26581      */
26582     setStore : function(store, initial){
26583         if(!initial && this.store){
26584             this.store.un("datachanged", this.refresh);
26585             this.store.un("add", this.onAdd);
26586             this.store.un("remove", this.onRemove);
26587             this.store.un("update", this.onUpdate);
26588             this.store.un("clear", this.refresh);
26589             this.store.un("beforeload", this.onBeforeLoad);
26590             this.store.un("load", this.onLoad);
26591             this.store.un("loadexception", this.onLoad);
26592         }
26593         if(store){
26594           
26595             store.on("datachanged", this.refresh, this);
26596             store.on("add", this.onAdd, this);
26597             store.on("remove", this.onRemove, this);
26598             store.on("update", this.onUpdate, this);
26599             store.on("clear", this.refresh, this);
26600             store.on("beforeload", this.onBeforeLoad, this);
26601             store.on("load", this.onLoad, this);
26602             store.on("loadexception", this.onLoad, this);
26603         }
26604         
26605         if(store){
26606             this.refresh();
26607         }
26608     },
26609     /**
26610      * onbeforeLoad - masks the loading area.
26611      *
26612      */
26613     onBeforeLoad : function(store,opts)
26614     {
26615          //Roo.log('onBeforeLoad');   
26616         if (!opts.add) {
26617             this.el.update("");
26618         }
26619         this.el.mask(this.mask ? this.mask : "Loading" ); 
26620     },
26621     onLoad : function ()
26622     {
26623         this.el.unmask();
26624     },
26625     
26626
26627     /**
26628      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26629      * @param {HTMLElement} node
26630      * @return {HTMLElement} The template node
26631      */
26632     findItemFromChild : function(node){
26633         var el = this.dataName  ?
26634             this.el.child('.roo-tpl-' + this.dataName,true) :
26635             this.el.dom; 
26636         
26637         if(!node || node.parentNode == el){
26638                     return node;
26639             }
26640             var p = node.parentNode;
26641             while(p && p != el){
26642             if(p.parentNode == el){
26643                 return p;
26644             }
26645             p = p.parentNode;
26646         }
26647             return null;
26648     },
26649
26650     /** @ignore */
26651     onClick : function(e){
26652         var item = this.findItemFromChild(e.getTarget());
26653         if(item){
26654             var index = this.indexOf(item);
26655             if(this.onItemClick(item, index, e) !== false){
26656                 this.fireEvent("click", this, index, item, e);
26657             }
26658         }else{
26659             this.clearSelections();
26660         }
26661     },
26662
26663     /** @ignore */
26664     onContextMenu : function(e){
26665         var item = this.findItemFromChild(e.getTarget());
26666         if(item){
26667             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26668         }
26669     },
26670
26671     /** @ignore */
26672     onDblClick : function(e){
26673         var item = this.findItemFromChild(e.getTarget());
26674         if(item){
26675             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26676         }
26677     },
26678
26679     onItemClick : function(item, index, e)
26680     {
26681         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26682             return false;
26683         }
26684         if (this.toggleSelect) {
26685             var m = this.isSelected(item) ? 'unselect' : 'select';
26686             //Roo.log(m);
26687             var _t = this;
26688             _t[m](item, true, false);
26689             return true;
26690         }
26691         if(this.multiSelect || this.singleSelect){
26692             if(this.multiSelect && e.shiftKey && this.lastSelection){
26693                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26694             }else{
26695                 this.select(item, this.multiSelect && e.ctrlKey);
26696                 this.lastSelection = item;
26697             }
26698             
26699             if(!this.tickable){
26700                 e.preventDefault();
26701             }
26702             
26703         }
26704         return true;
26705     },
26706
26707     /**
26708      * Get the number of selected nodes.
26709      * @return {Number}
26710      */
26711     getSelectionCount : function(){
26712         return this.selections.length;
26713     },
26714
26715     /**
26716      * Get the currently selected nodes.
26717      * @return {Array} An array of HTMLElements
26718      */
26719     getSelectedNodes : function(){
26720         return this.selections;
26721     },
26722
26723     /**
26724      * Get the indexes of the selected nodes.
26725      * @return {Array}
26726      */
26727     getSelectedIndexes : function(){
26728         var indexes = [], s = this.selections;
26729         for(var i = 0, len = s.length; i < len; i++){
26730             indexes.push(s[i].nodeIndex);
26731         }
26732         return indexes;
26733     },
26734
26735     /**
26736      * Clear all selections
26737      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26738      */
26739     clearSelections : function(suppressEvent){
26740         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26741             this.cmp.elements = this.selections;
26742             this.cmp.removeClass(this.selectedClass);
26743             this.selections = [];
26744             if(!suppressEvent){
26745                 this.fireEvent("selectionchange", this, this.selections);
26746             }
26747         }
26748     },
26749
26750     /**
26751      * Returns true if the passed node is selected
26752      * @param {HTMLElement/Number} node The node or node index
26753      * @return {Boolean}
26754      */
26755     isSelected : function(node){
26756         var s = this.selections;
26757         if(s.length < 1){
26758             return false;
26759         }
26760         node = this.getNode(node);
26761         return s.indexOf(node) !== -1;
26762     },
26763
26764     /**
26765      * Selects nodes.
26766      * @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
26767      * @param {Boolean} keepExisting (optional) true to keep existing selections
26768      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26769      */
26770     select : function(nodeInfo, keepExisting, suppressEvent){
26771         if(nodeInfo instanceof Array){
26772             if(!keepExisting){
26773                 this.clearSelections(true);
26774             }
26775             for(var i = 0, len = nodeInfo.length; i < len; i++){
26776                 this.select(nodeInfo[i], true, true);
26777             }
26778             return;
26779         } 
26780         var node = this.getNode(nodeInfo);
26781         if(!node || this.isSelected(node)){
26782             return; // already selected.
26783         }
26784         if(!keepExisting){
26785             this.clearSelections(true);
26786         }
26787         
26788         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26789             Roo.fly(node).addClass(this.selectedClass);
26790             this.selections.push(node);
26791             if(!suppressEvent){
26792                 this.fireEvent("selectionchange", this, this.selections);
26793             }
26794         }
26795         
26796         
26797     },
26798       /**
26799      * Unselects nodes.
26800      * @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
26801      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26802      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26803      */
26804     unselect : function(nodeInfo, keepExisting, suppressEvent)
26805     {
26806         if(nodeInfo instanceof Array){
26807             Roo.each(this.selections, function(s) {
26808                 this.unselect(s, nodeInfo);
26809             }, this);
26810             return;
26811         }
26812         var node = this.getNode(nodeInfo);
26813         if(!node || !this.isSelected(node)){
26814             //Roo.log("not selected");
26815             return; // not selected.
26816         }
26817         // fireevent???
26818         var ns = [];
26819         Roo.each(this.selections, function(s) {
26820             if (s == node ) {
26821                 Roo.fly(node).removeClass(this.selectedClass);
26822
26823                 return;
26824             }
26825             ns.push(s);
26826         },this);
26827         
26828         this.selections= ns;
26829         this.fireEvent("selectionchange", this, this.selections);
26830     },
26831
26832     /**
26833      * Gets a template node.
26834      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26835      * @return {HTMLElement} The node or null if it wasn't found
26836      */
26837     getNode : function(nodeInfo){
26838         if(typeof nodeInfo == "string"){
26839             return document.getElementById(nodeInfo);
26840         }else if(typeof nodeInfo == "number"){
26841             return this.nodes[nodeInfo];
26842         }
26843         return nodeInfo;
26844     },
26845
26846     /**
26847      * Gets a range template nodes.
26848      * @param {Number} startIndex
26849      * @param {Number} endIndex
26850      * @return {Array} An array of nodes
26851      */
26852     getNodes : function(start, end){
26853         var ns = this.nodes;
26854         start = start || 0;
26855         end = typeof end == "undefined" ? ns.length - 1 : end;
26856         var nodes = [];
26857         if(start <= end){
26858             for(var i = start; i <= end; i++){
26859                 nodes.push(ns[i]);
26860             }
26861         } else{
26862             for(var i = start; i >= end; i--){
26863                 nodes.push(ns[i]);
26864             }
26865         }
26866         return nodes;
26867     },
26868
26869     /**
26870      * Finds the index of the passed node
26871      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26872      * @return {Number} The index of the node or -1
26873      */
26874     indexOf : function(node){
26875         node = this.getNode(node);
26876         if(typeof node.nodeIndex == "number"){
26877             return node.nodeIndex;
26878         }
26879         var ns = this.nodes;
26880         for(var i = 0, len = ns.length; i < len; i++){
26881             if(ns[i] == node){
26882                 return i;
26883             }
26884         }
26885         return -1;
26886     }
26887 });
26888 /*
26889  * Based on:
26890  * Ext JS Library 1.1.1
26891  * Copyright(c) 2006-2007, Ext JS, LLC.
26892  *
26893  * Originally Released Under LGPL - original licence link has changed is not relivant.
26894  *
26895  * Fork - LGPL
26896  * <script type="text/javascript">
26897  */
26898
26899 /**
26900  * @class Roo.JsonView
26901  * @extends Roo.View
26902  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
26903 <pre><code>
26904 var view = new Roo.JsonView({
26905     container: "my-element",
26906     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
26907     multiSelect: true, 
26908     jsonRoot: "data" 
26909 });
26910
26911 // listen for node click?
26912 view.on("click", function(vw, index, node, e){
26913     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26914 });
26915
26916 // direct load of JSON data
26917 view.load("foobar.php");
26918
26919 // Example from my blog list
26920 var tpl = new Roo.Template(
26921     '&lt;div class="entry"&gt;' +
26922     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
26923     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
26924     "&lt;/div&gt;&lt;hr /&gt;"
26925 );
26926
26927 var moreView = new Roo.JsonView({
26928     container :  "entry-list", 
26929     template : tpl,
26930     jsonRoot: "posts"
26931 });
26932 moreView.on("beforerender", this.sortEntries, this);
26933 moreView.load({
26934     url: "/blog/get-posts.php",
26935     params: "allposts=true",
26936     text: "Loading Blog Entries..."
26937 });
26938 </code></pre>
26939
26940 * Note: old code is supported with arguments : (container, template, config)
26941
26942
26943  * @constructor
26944  * Create a new JsonView
26945  * 
26946  * @param {Object} config The config object
26947  * 
26948  */
26949 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
26950     
26951     
26952     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
26953
26954     var um = this.el.getUpdateManager();
26955     um.setRenderer(this);
26956     um.on("update", this.onLoad, this);
26957     um.on("failure", this.onLoadException, this);
26958
26959     /**
26960      * @event beforerender
26961      * Fires before rendering of the downloaded JSON data.
26962      * @param {Roo.JsonView} this
26963      * @param {Object} data The JSON data loaded
26964      */
26965     /**
26966      * @event load
26967      * Fires when data is loaded.
26968      * @param {Roo.JsonView} this
26969      * @param {Object} data The JSON data loaded
26970      * @param {Object} response The raw Connect response object
26971      */
26972     /**
26973      * @event loadexception
26974      * Fires when loading fails.
26975      * @param {Roo.JsonView} this
26976      * @param {Object} response The raw Connect response object
26977      */
26978     this.addEvents({
26979         'beforerender' : true,
26980         'load' : true,
26981         'loadexception' : true
26982     });
26983 };
26984 Roo.extend(Roo.JsonView, Roo.View, {
26985     /**
26986      * @type {String} The root property in the loaded JSON object that contains the data
26987      */
26988     jsonRoot : "",
26989
26990     /**
26991      * Refreshes the view.
26992      */
26993     refresh : function(){
26994         this.clearSelections();
26995         this.el.update("");
26996         var html = [];
26997         var o = this.jsonData;
26998         if(o && o.length > 0){
26999             for(var i = 0, len = o.length; i < len; i++){
27000                 var data = this.prepareData(o[i], i, o);
27001                 html[html.length] = this.tpl.apply(data);
27002             }
27003         }else{
27004             html.push(this.emptyText);
27005         }
27006         this.el.update(html.join(""));
27007         this.nodes = this.el.dom.childNodes;
27008         this.updateIndexes(0);
27009     },
27010
27011     /**
27012      * 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.
27013      * @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:
27014      <pre><code>
27015      view.load({
27016          url: "your-url.php",
27017          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27018          callback: yourFunction,
27019          scope: yourObject, //(optional scope)
27020          discardUrl: false,
27021          nocache: false,
27022          text: "Loading...",
27023          timeout: 30,
27024          scripts: false
27025      });
27026      </code></pre>
27027      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27028      * 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.
27029      * @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}
27030      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27031      * @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.
27032      */
27033     load : function(){
27034         var um = this.el.getUpdateManager();
27035         um.update.apply(um, arguments);
27036     },
27037
27038     // note - render is a standard framework call...
27039     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27040     render : function(el, response){
27041         
27042         this.clearSelections();
27043         this.el.update("");
27044         var o;
27045         try{
27046             if (response != '') {
27047                 o = Roo.util.JSON.decode(response.responseText);
27048                 if(this.jsonRoot){
27049                     
27050                     o = o[this.jsonRoot];
27051                 }
27052             }
27053         } catch(e){
27054         }
27055         /**
27056          * The current JSON data or null
27057          */
27058         this.jsonData = o;
27059         this.beforeRender();
27060         this.refresh();
27061     },
27062
27063 /**
27064  * Get the number of records in the current JSON dataset
27065  * @return {Number}
27066  */
27067     getCount : function(){
27068         return this.jsonData ? this.jsonData.length : 0;
27069     },
27070
27071 /**
27072  * Returns the JSON object for the specified node(s)
27073  * @param {HTMLElement/Array} node The node or an array of nodes
27074  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27075  * you get the JSON object for the node
27076  */
27077     getNodeData : function(node){
27078         if(node instanceof Array){
27079             var data = [];
27080             for(var i = 0, len = node.length; i < len; i++){
27081                 data.push(this.getNodeData(node[i]));
27082             }
27083             return data;
27084         }
27085         return this.jsonData[this.indexOf(node)] || null;
27086     },
27087
27088     beforeRender : function(){
27089         this.snapshot = this.jsonData;
27090         if(this.sortInfo){
27091             this.sort.apply(this, this.sortInfo);
27092         }
27093         this.fireEvent("beforerender", this, this.jsonData);
27094     },
27095
27096     onLoad : function(el, o){
27097         this.fireEvent("load", this, this.jsonData, o);
27098     },
27099
27100     onLoadException : function(el, o){
27101         this.fireEvent("loadexception", this, o);
27102     },
27103
27104 /**
27105  * Filter the data by a specific property.
27106  * @param {String} property A property on your JSON objects
27107  * @param {String/RegExp} value Either string that the property values
27108  * should start with, or a RegExp to test against the property
27109  */
27110     filter : function(property, value){
27111         if(this.jsonData){
27112             var data = [];
27113             var ss = this.snapshot;
27114             if(typeof value == "string"){
27115                 var vlen = value.length;
27116                 if(vlen == 0){
27117                     this.clearFilter();
27118                     return;
27119                 }
27120                 value = value.toLowerCase();
27121                 for(var i = 0, len = ss.length; i < len; i++){
27122                     var o = ss[i];
27123                     if(o[property].substr(0, vlen).toLowerCase() == value){
27124                         data.push(o);
27125                     }
27126                 }
27127             } else if(value.exec){ // regex?
27128                 for(var i = 0, len = ss.length; i < len; i++){
27129                     var o = ss[i];
27130                     if(value.test(o[property])){
27131                         data.push(o);
27132                     }
27133                 }
27134             } else{
27135                 return;
27136             }
27137             this.jsonData = data;
27138             this.refresh();
27139         }
27140     },
27141
27142 /**
27143  * Filter by a function. The passed function will be called with each
27144  * object in the current dataset. If the function returns true the value is kept,
27145  * otherwise it is filtered.
27146  * @param {Function} fn
27147  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27148  */
27149     filterBy : function(fn, scope){
27150         if(this.jsonData){
27151             var data = [];
27152             var ss = this.snapshot;
27153             for(var i = 0, len = ss.length; i < len; i++){
27154                 var o = ss[i];
27155                 if(fn.call(scope || this, o)){
27156                     data.push(o);
27157                 }
27158             }
27159             this.jsonData = data;
27160             this.refresh();
27161         }
27162     },
27163
27164 /**
27165  * Clears the current filter.
27166  */
27167     clearFilter : function(){
27168         if(this.snapshot && this.jsonData != this.snapshot){
27169             this.jsonData = this.snapshot;
27170             this.refresh();
27171         }
27172     },
27173
27174
27175 /**
27176  * Sorts the data for this view and refreshes it.
27177  * @param {String} property A property on your JSON objects to sort on
27178  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27179  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27180  */
27181     sort : function(property, dir, sortType){
27182         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27183         if(this.jsonData){
27184             var p = property;
27185             var dsc = dir && dir.toLowerCase() == "desc";
27186             var f = function(o1, o2){
27187                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27188                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27189                 ;
27190                 if(v1 < v2){
27191                     return dsc ? +1 : -1;
27192                 } else if(v1 > v2){
27193                     return dsc ? -1 : +1;
27194                 } else{
27195                     return 0;
27196                 }
27197             };
27198             this.jsonData.sort(f);
27199             this.refresh();
27200             if(this.jsonData != this.snapshot){
27201                 this.snapshot.sort(f);
27202             }
27203         }
27204     }
27205 });/*
27206  * Based on:
27207  * Ext JS Library 1.1.1
27208  * Copyright(c) 2006-2007, Ext JS, LLC.
27209  *
27210  * Originally Released Under LGPL - original licence link has changed is not relivant.
27211  *
27212  * Fork - LGPL
27213  * <script type="text/javascript">
27214  */
27215  
27216
27217 /**
27218  * @class Roo.ColorPalette
27219  * @extends Roo.Component
27220  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27221  * Here's an example of typical usage:
27222  * <pre><code>
27223 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27224 cp.render('my-div');
27225
27226 cp.on('select', function(palette, selColor){
27227     // do something with selColor
27228 });
27229 </code></pre>
27230  * @constructor
27231  * Create a new ColorPalette
27232  * @param {Object} config The config object
27233  */
27234 Roo.ColorPalette = function(config){
27235     Roo.ColorPalette.superclass.constructor.call(this, config);
27236     this.addEvents({
27237         /**
27238              * @event select
27239              * Fires when a color is selected
27240              * @param {ColorPalette} this
27241              * @param {String} color The 6-digit color hex code (without the # symbol)
27242              */
27243         select: true
27244     });
27245
27246     if(this.handler){
27247         this.on("select", this.handler, this.scope, true);
27248     }
27249 };
27250 Roo.extend(Roo.ColorPalette, Roo.Component, {
27251     /**
27252      * @cfg {String} itemCls
27253      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27254      */
27255     itemCls : "x-color-palette",
27256     /**
27257      * @cfg {String} value
27258      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27259      * the hex codes are case-sensitive.
27260      */
27261     value : null,
27262     clickEvent:'click',
27263     // private
27264     ctype: "Roo.ColorPalette",
27265
27266     /**
27267      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27268      */
27269     allowReselect : false,
27270
27271     /**
27272      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27273      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27274      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27275      * of colors with the width setting until the box is symmetrical.</p>
27276      * <p>You can override individual colors if needed:</p>
27277      * <pre><code>
27278 var cp = new Roo.ColorPalette();
27279 cp.colors[0] = "FF0000";  // change the first box to red
27280 </code></pre>
27281
27282 Or you can provide a custom array of your own for complete control:
27283 <pre><code>
27284 var cp = new Roo.ColorPalette();
27285 cp.colors = ["000000", "993300", "333300"];
27286 </code></pre>
27287      * @type Array
27288      */
27289     colors : [
27290         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27291         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27292         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27293         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27294         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27295     ],
27296
27297     // private
27298     onRender : function(container, position){
27299         var t = new Roo.MasterTemplate(
27300             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27301         );
27302         var c = this.colors;
27303         for(var i = 0, len = c.length; i < len; i++){
27304             t.add([c[i]]);
27305         }
27306         var el = document.createElement("div");
27307         el.className = this.itemCls;
27308         t.overwrite(el);
27309         container.dom.insertBefore(el, position);
27310         this.el = Roo.get(el);
27311         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27312         if(this.clickEvent != 'click'){
27313             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27314         }
27315     },
27316
27317     // private
27318     afterRender : function(){
27319         Roo.ColorPalette.superclass.afterRender.call(this);
27320         if(this.value){
27321             var s = this.value;
27322             this.value = null;
27323             this.select(s);
27324         }
27325     },
27326
27327     // private
27328     handleClick : function(e, t){
27329         e.preventDefault();
27330         if(!this.disabled){
27331             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27332             this.select(c.toUpperCase());
27333         }
27334     },
27335
27336     /**
27337      * Selects the specified color in the palette (fires the select event)
27338      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27339      */
27340     select : function(color){
27341         color = color.replace("#", "");
27342         if(color != this.value || this.allowReselect){
27343             var el = this.el;
27344             if(this.value){
27345                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27346             }
27347             el.child("a.color-"+color).addClass("x-color-palette-sel");
27348             this.value = color;
27349             this.fireEvent("select", this, color);
27350         }
27351     }
27352 });/*
27353  * Based on:
27354  * Ext JS Library 1.1.1
27355  * Copyright(c) 2006-2007, Ext JS, LLC.
27356  *
27357  * Originally Released Under LGPL - original licence link has changed is not relivant.
27358  *
27359  * Fork - LGPL
27360  * <script type="text/javascript">
27361  */
27362  
27363 /**
27364  * @class Roo.DatePicker
27365  * @extends Roo.Component
27366  * Simple date picker class.
27367  * @constructor
27368  * Create a new DatePicker
27369  * @param {Object} config The config object
27370  */
27371 Roo.DatePicker = function(config){
27372     Roo.DatePicker.superclass.constructor.call(this, config);
27373
27374     this.value = config && config.value ?
27375                  config.value.clearTime() : new Date().clearTime();
27376
27377     this.addEvents({
27378         /**
27379              * @event select
27380              * Fires when a date is selected
27381              * @param {DatePicker} this
27382              * @param {Date} date The selected date
27383              */
27384         'select': true,
27385         /**
27386              * @event monthchange
27387              * Fires when the displayed month changes 
27388              * @param {DatePicker} this
27389              * @param {Date} date The selected month
27390              */
27391         'monthchange': true
27392     });
27393
27394     if(this.handler){
27395         this.on("select", this.handler,  this.scope || this);
27396     }
27397     // build the disabledDatesRE
27398     if(!this.disabledDatesRE && this.disabledDates){
27399         var dd = this.disabledDates;
27400         var re = "(?:";
27401         for(var i = 0; i < dd.length; i++){
27402             re += dd[i];
27403             if(i != dd.length-1) {
27404                 re += "|";
27405             }
27406         }
27407         this.disabledDatesRE = new RegExp(re + ")");
27408     }
27409 };
27410
27411 Roo.extend(Roo.DatePicker, Roo.Component, {
27412     /**
27413      * @cfg {String} todayText
27414      * The text to display on the button that selects the current date (defaults to "Today")
27415      */
27416     todayText : "Today",
27417     /**
27418      * @cfg {String} okText
27419      * The text to display on the ok button
27420      */
27421     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27422     /**
27423      * @cfg {String} cancelText
27424      * The text to display on the cancel button
27425      */
27426     cancelText : "Cancel",
27427     /**
27428      * @cfg {String} todayTip
27429      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27430      */
27431     todayTip : "{0} (Spacebar)",
27432     /**
27433      * @cfg {Date} minDate
27434      * Minimum allowable date (JavaScript date object, defaults to null)
27435      */
27436     minDate : null,
27437     /**
27438      * @cfg {Date} maxDate
27439      * Maximum allowable date (JavaScript date object, defaults to null)
27440      */
27441     maxDate : null,
27442     /**
27443      * @cfg {String} minText
27444      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27445      */
27446     minText : "This date is before the minimum date",
27447     /**
27448      * @cfg {String} maxText
27449      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27450      */
27451     maxText : "This date is after the maximum date",
27452     /**
27453      * @cfg {String} format
27454      * The default date format string which can be overriden for localization support.  The format must be
27455      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27456      */
27457     format : "m/d/y",
27458     /**
27459      * @cfg {Array} disabledDays
27460      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27461      */
27462     disabledDays : null,
27463     /**
27464      * @cfg {String} disabledDaysText
27465      * The tooltip to display when the date falls on a disabled day (defaults to "")
27466      */
27467     disabledDaysText : "",
27468     /**
27469      * @cfg {RegExp} disabledDatesRE
27470      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27471      */
27472     disabledDatesRE : null,
27473     /**
27474      * @cfg {String} disabledDatesText
27475      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27476      */
27477     disabledDatesText : "",
27478     /**
27479      * @cfg {Boolean} constrainToViewport
27480      * True to constrain the date picker to the viewport (defaults to true)
27481      */
27482     constrainToViewport : true,
27483     /**
27484      * @cfg {Array} monthNames
27485      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27486      */
27487     monthNames : Date.monthNames,
27488     /**
27489      * @cfg {Array} dayNames
27490      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27491      */
27492     dayNames : Date.dayNames,
27493     /**
27494      * @cfg {String} nextText
27495      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27496      */
27497     nextText: 'Next Month (Control+Right)',
27498     /**
27499      * @cfg {String} prevText
27500      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27501      */
27502     prevText: 'Previous Month (Control+Left)',
27503     /**
27504      * @cfg {String} monthYearText
27505      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27506      */
27507     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27508     /**
27509      * @cfg {Number} startDay
27510      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27511      */
27512     startDay : 0,
27513     /**
27514      * @cfg {Bool} showClear
27515      * Show a clear button (usefull for date form elements that can be blank.)
27516      */
27517     
27518     showClear: false,
27519     
27520     /**
27521      * Sets the value of the date field
27522      * @param {Date} value The date to set
27523      */
27524     setValue : function(value){
27525         var old = this.value;
27526         
27527         if (typeof(value) == 'string') {
27528          
27529             value = Date.parseDate(value, this.format);
27530         }
27531         if (!value) {
27532             value = new Date();
27533         }
27534         
27535         this.value = value.clearTime(true);
27536         if(this.el){
27537             this.update(this.value);
27538         }
27539     },
27540
27541     /**
27542      * Gets the current selected value of the date field
27543      * @return {Date} The selected date
27544      */
27545     getValue : function(){
27546         return this.value;
27547     },
27548
27549     // private
27550     focus : function(){
27551         if(this.el){
27552             this.update(this.activeDate);
27553         }
27554     },
27555
27556     // privateval
27557     onRender : function(container, position){
27558         
27559         var m = [
27560              '<table cellspacing="0">',
27561                 '<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>',
27562                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27563         var dn = this.dayNames;
27564         for(var i = 0; i < 7; i++){
27565             var d = this.startDay+i;
27566             if(d > 6){
27567                 d = d-7;
27568             }
27569             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27570         }
27571         m[m.length] = "</tr></thead><tbody><tr>";
27572         for(var i = 0; i < 42; i++) {
27573             if(i % 7 == 0 && i != 0){
27574                 m[m.length] = "</tr><tr>";
27575             }
27576             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27577         }
27578         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27579             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27580
27581         var el = document.createElement("div");
27582         el.className = "x-date-picker";
27583         el.innerHTML = m.join("");
27584
27585         container.dom.insertBefore(el, position);
27586
27587         this.el = Roo.get(el);
27588         this.eventEl = Roo.get(el.firstChild);
27589
27590         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27591             handler: this.showPrevMonth,
27592             scope: this,
27593             preventDefault:true,
27594             stopDefault:true
27595         });
27596
27597         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27598             handler: this.showNextMonth,
27599             scope: this,
27600             preventDefault:true,
27601             stopDefault:true
27602         });
27603
27604         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27605
27606         this.monthPicker = this.el.down('div.x-date-mp');
27607         this.monthPicker.enableDisplayMode('block');
27608         
27609         var kn = new Roo.KeyNav(this.eventEl, {
27610             "left" : function(e){
27611                 e.ctrlKey ?
27612                     this.showPrevMonth() :
27613                     this.update(this.activeDate.add("d", -1));
27614             },
27615
27616             "right" : function(e){
27617                 e.ctrlKey ?
27618                     this.showNextMonth() :
27619                     this.update(this.activeDate.add("d", 1));
27620             },
27621
27622             "up" : function(e){
27623                 e.ctrlKey ?
27624                     this.showNextYear() :
27625                     this.update(this.activeDate.add("d", -7));
27626             },
27627
27628             "down" : function(e){
27629                 e.ctrlKey ?
27630                     this.showPrevYear() :
27631                     this.update(this.activeDate.add("d", 7));
27632             },
27633
27634             "pageUp" : function(e){
27635                 this.showNextMonth();
27636             },
27637
27638             "pageDown" : function(e){
27639                 this.showPrevMonth();
27640             },
27641
27642             "enter" : function(e){
27643                 e.stopPropagation();
27644                 return true;
27645             },
27646
27647             scope : this
27648         });
27649
27650         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27651
27652         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27653
27654         this.el.unselectable();
27655         
27656         this.cells = this.el.select("table.x-date-inner tbody td");
27657         this.textNodes = this.el.query("table.x-date-inner tbody span");
27658
27659         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27660             text: "&#160;",
27661             tooltip: this.monthYearText
27662         });
27663
27664         this.mbtn.on('click', this.showMonthPicker, this);
27665         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27666
27667
27668         var today = (new Date()).dateFormat(this.format);
27669         
27670         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27671         if (this.showClear) {
27672             baseTb.add( new Roo.Toolbar.Fill());
27673         }
27674         baseTb.add({
27675             text: String.format(this.todayText, today),
27676             tooltip: String.format(this.todayTip, today),
27677             handler: this.selectToday,
27678             scope: this
27679         });
27680         
27681         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27682             
27683         //});
27684         if (this.showClear) {
27685             
27686             baseTb.add( new Roo.Toolbar.Fill());
27687             baseTb.add({
27688                 text: '&#160;',
27689                 cls: 'x-btn-icon x-btn-clear',
27690                 handler: function() {
27691                     //this.value = '';
27692                     this.fireEvent("select", this, '');
27693                 },
27694                 scope: this
27695             });
27696         }
27697         
27698         
27699         if(Roo.isIE){
27700             this.el.repaint();
27701         }
27702         this.update(this.value);
27703     },
27704
27705     createMonthPicker : function(){
27706         if(!this.monthPicker.dom.firstChild){
27707             var buf = ['<table border="0" cellspacing="0">'];
27708             for(var i = 0; i < 6; i++){
27709                 buf.push(
27710                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27711                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27712                     i == 0 ?
27713                     '<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>' :
27714                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27715                 );
27716             }
27717             buf.push(
27718                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27719                     this.okText,
27720                     '</button><button type="button" class="x-date-mp-cancel">',
27721                     this.cancelText,
27722                     '</button></td></tr>',
27723                 '</table>'
27724             );
27725             this.monthPicker.update(buf.join(''));
27726             this.monthPicker.on('click', this.onMonthClick, this);
27727             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27728
27729             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27730             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27731
27732             this.mpMonths.each(function(m, a, i){
27733                 i += 1;
27734                 if((i%2) == 0){
27735                     m.dom.xmonth = 5 + Math.round(i * .5);
27736                 }else{
27737                     m.dom.xmonth = Math.round((i-1) * .5);
27738                 }
27739             });
27740         }
27741     },
27742
27743     showMonthPicker : function(){
27744         this.createMonthPicker();
27745         var size = this.el.getSize();
27746         this.monthPicker.setSize(size);
27747         this.monthPicker.child('table').setSize(size);
27748
27749         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27750         this.updateMPMonth(this.mpSelMonth);
27751         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27752         this.updateMPYear(this.mpSelYear);
27753
27754         this.monthPicker.slideIn('t', {duration:.2});
27755     },
27756
27757     updateMPYear : function(y){
27758         this.mpyear = y;
27759         var ys = this.mpYears.elements;
27760         for(var i = 1; i <= 10; i++){
27761             var td = ys[i-1], y2;
27762             if((i%2) == 0){
27763                 y2 = y + Math.round(i * .5);
27764                 td.firstChild.innerHTML = y2;
27765                 td.xyear = y2;
27766             }else{
27767                 y2 = y - (5-Math.round(i * .5));
27768                 td.firstChild.innerHTML = y2;
27769                 td.xyear = y2;
27770             }
27771             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27772         }
27773     },
27774
27775     updateMPMonth : function(sm){
27776         this.mpMonths.each(function(m, a, i){
27777             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27778         });
27779     },
27780
27781     selectMPMonth: function(m){
27782         
27783     },
27784
27785     onMonthClick : function(e, t){
27786         e.stopEvent();
27787         var el = new Roo.Element(t), pn;
27788         if(el.is('button.x-date-mp-cancel')){
27789             this.hideMonthPicker();
27790         }
27791         else if(el.is('button.x-date-mp-ok')){
27792             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27793             this.hideMonthPicker();
27794         }
27795         else if(pn = el.up('td.x-date-mp-month', 2)){
27796             this.mpMonths.removeClass('x-date-mp-sel');
27797             pn.addClass('x-date-mp-sel');
27798             this.mpSelMonth = pn.dom.xmonth;
27799         }
27800         else if(pn = el.up('td.x-date-mp-year', 2)){
27801             this.mpYears.removeClass('x-date-mp-sel');
27802             pn.addClass('x-date-mp-sel');
27803             this.mpSelYear = pn.dom.xyear;
27804         }
27805         else if(el.is('a.x-date-mp-prev')){
27806             this.updateMPYear(this.mpyear-10);
27807         }
27808         else if(el.is('a.x-date-mp-next')){
27809             this.updateMPYear(this.mpyear+10);
27810         }
27811     },
27812
27813     onMonthDblClick : function(e, t){
27814         e.stopEvent();
27815         var el = new Roo.Element(t), pn;
27816         if(pn = el.up('td.x-date-mp-month', 2)){
27817             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27818             this.hideMonthPicker();
27819         }
27820         else if(pn = el.up('td.x-date-mp-year', 2)){
27821             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27822             this.hideMonthPicker();
27823         }
27824     },
27825
27826     hideMonthPicker : function(disableAnim){
27827         if(this.monthPicker){
27828             if(disableAnim === true){
27829                 this.monthPicker.hide();
27830             }else{
27831                 this.monthPicker.slideOut('t', {duration:.2});
27832             }
27833         }
27834     },
27835
27836     // private
27837     showPrevMonth : function(e){
27838         this.update(this.activeDate.add("mo", -1));
27839     },
27840
27841     // private
27842     showNextMonth : function(e){
27843         this.update(this.activeDate.add("mo", 1));
27844     },
27845
27846     // private
27847     showPrevYear : function(){
27848         this.update(this.activeDate.add("y", -1));
27849     },
27850
27851     // private
27852     showNextYear : function(){
27853         this.update(this.activeDate.add("y", 1));
27854     },
27855
27856     // private
27857     handleMouseWheel : function(e){
27858         var delta = e.getWheelDelta();
27859         if(delta > 0){
27860             this.showPrevMonth();
27861             e.stopEvent();
27862         } else if(delta < 0){
27863             this.showNextMonth();
27864             e.stopEvent();
27865         }
27866     },
27867
27868     // private
27869     handleDateClick : function(e, t){
27870         e.stopEvent();
27871         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
27872             this.setValue(new Date(t.dateValue));
27873             this.fireEvent("select", this, this.value);
27874         }
27875     },
27876
27877     // private
27878     selectToday : function(){
27879         this.setValue(new Date().clearTime());
27880         this.fireEvent("select", this, this.value);
27881     },
27882
27883     // private
27884     update : function(date)
27885     {
27886         var vd = this.activeDate;
27887         this.activeDate = date;
27888         if(vd && this.el){
27889             var t = date.getTime();
27890             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
27891                 this.cells.removeClass("x-date-selected");
27892                 this.cells.each(function(c){
27893                    if(c.dom.firstChild.dateValue == t){
27894                        c.addClass("x-date-selected");
27895                        setTimeout(function(){
27896                             try{c.dom.firstChild.focus();}catch(e){}
27897                        }, 50);
27898                        return false;
27899                    }
27900                 });
27901                 return;
27902             }
27903         }
27904         
27905         var days = date.getDaysInMonth();
27906         var firstOfMonth = date.getFirstDateOfMonth();
27907         var startingPos = firstOfMonth.getDay()-this.startDay;
27908
27909         if(startingPos <= this.startDay){
27910             startingPos += 7;
27911         }
27912
27913         var pm = date.add("mo", -1);
27914         var prevStart = pm.getDaysInMonth()-startingPos;
27915
27916         var cells = this.cells.elements;
27917         var textEls = this.textNodes;
27918         days += startingPos;
27919
27920         // convert everything to numbers so it's fast
27921         var day = 86400000;
27922         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
27923         var today = new Date().clearTime().getTime();
27924         var sel = date.clearTime().getTime();
27925         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
27926         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
27927         var ddMatch = this.disabledDatesRE;
27928         var ddText = this.disabledDatesText;
27929         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
27930         var ddaysText = this.disabledDaysText;
27931         var format = this.format;
27932
27933         var setCellClass = function(cal, cell){
27934             cell.title = "";
27935             var t = d.getTime();
27936             cell.firstChild.dateValue = t;
27937             if(t == today){
27938                 cell.className += " x-date-today";
27939                 cell.title = cal.todayText;
27940             }
27941             if(t == sel){
27942                 cell.className += " x-date-selected";
27943                 setTimeout(function(){
27944                     try{cell.firstChild.focus();}catch(e){}
27945                 }, 50);
27946             }
27947             // disabling
27948             if(t < min) {
27949                 cell.className = " x-date-disabled";
27950                 cell.title = cal.minText;
27951                 return;
27952             }
27953             if(t > max) {
27954                 cell.className = " x-date-disabled";
27955                 cell.title = cal.maxText;
27956                 return;
27957             }
27958             if(ddays){
27959                 if(ddays.indexOf(d.getDay()) != -1){
27960                     cell.title = ddaysText;
27961                     cell.className = " x-date-disabled";
27962                 }
27963             }
27964             if(ddMatch && format){
27965                 var fvalue = d.dateFormat(format);
27966                 if(ddMatch.test(fvalue)){
27967                     cell.title = ddText.replace("%0", fvalue);
27968                     cell.className = " x-date-disabled";
27969                 }
27970             }
27971         };
27972
27973         var i = 0;
27974         for(; i < startingPos; i++) {
27975             textEls[i].innerHTML = (++prevStart);
27976             d.setDate(d.getDate()+1);
27977             cells[i].className = "x-date-prevday";
27978             setCellClass(this, cells[i]);
27979         }
27980         for(; i < days; i++){
27981             intDay = i - startingPos + 1;
27982             textEls[i].innerHTML = (intDay);
27983             d.setDate(d.getDate()+1);
27984             cells[i].className = "x-date-active";
27985             setCellClass(this, cells[i]);
27986         }
27987         var extraDays = 0;
27988         for(; i < 42; i++) {
27989              textEls[i].innerHTML = (++extraDays);
27990              d.setDate(d.getDate()+1);
27991              cells[i].className = "x-date-nextday";
27992              setCellClass(this, cells[i]);
27993         }
27994
27995         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
27996         this.fireEvent('monthchange', this, date);
27997         
27998         if(!this.internalRender){
27999             var main = this.el.dom.firstChild;
28000             var w = main.offsetWidth;
28001             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28002             Roo.fly(main).setWidth(w);
28003             this.internalRender = true;
28004             // opera does not respect the auto grow header center column
28005             // then, after it gets a width opera refuses to recalculate
28006             // without a second pass
28007             if(Roo.isOpera && !this.secondPass){
28008                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28009                 this.secondPass = true;
28010                 this.update.defer(10, this, [date]);
28011             }
28012         }
28013         
28014         
28015     }
28016 });        /*
28017  * Based on:
28018  * Ext JS Library 1.1.1
28019  * Copyright(c) 2006-2007, Ext JS, LLC.
28020  *
28021  * Originally Released Under LGPL - original licence link has changed is not relivant.
28022  *
28023  * Fork - LGPL
28024  * <script type="text/javascript">
28025  */
28026 /**
28027  * @class Roo.TabPanel
28028  * @extends Roo.util.Observable
28029  * A lightweight tab container.
28030  * <br><br>
28031  * Usage:
28032  * <pre><code>
28033 // basic tabs 1, built from existing content
28034 var tabs = new Roo.TabPanel("tabs1");
28035 tabs.addTab("script", "View Script");
28036 tabs.addTab("markup", "View Markup");
28037 tabs.activate("script");
28038
28039 // more advanced tabs, built from javascript
28040 var jtabs = new Roo.TabPanel("jtabs");
28041 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28042
28043 // set up the UpdateManager
28044 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28045 var updater = tab2.getUpdateManager();
28046 updater.setDefaultUrl("ajax1.htm");
28047 tab2.on('activate', updater.refresh, updater, true);
28048
28049 // Use setUrl for Ajax loading
28050 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28051 tab3.setUrl("ajax2.htm", null, true);
28052
28053 // Disabled tab
28054 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28055 tab4.disable();
28056
28057 jtabs.activate("jtabs-1");
28058  * </code></pre>
28059  * @constructor
28060  * Create a new TabPanel.
28061  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28062  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28063  */
28064 Roo.TabPanel = function(container, config){
28065     /**
28066     * The container element for this TabPanel.
28067     * @type Roo.Element
28068     */
28069     this.el = Roo.get(container, true);
28070     if(config){
28071         if(typeof config == "boolean"){
28072             this.tabPosition = config ? "bottom" : "top";
28073         }else{
28074             Roo.apply(this, config);
28075         }
28076     }
28077     if(this.tabPosition == "bottom"){
28078         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28079         this.el.addClass("x-tabs-bottom");
28080     }
28081     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28082     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28083     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28084     if(Roo.isIE){
28085         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28086     }
28087     if(this.tabPosition != "bottom"){
28088         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28089          * @type Roo.Element
28090          */
28091         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28092         this.el.addClass("x-tabs-top");
28093     }
28094     this.items = [];
28095
28096     this.bodyEl.setStyle("position", "relative");
28097
28098     this.active = null;
28099     this.activateDelegate = this.activate.createDelegate(this);
28100
28101     this.addEvents({
28102         /**
28103          * @event tabchange
28104          * Fires when the active tab changes
28105          * @param {Roo.TabPanel} this
28106          * @param {Roo.TabPanelItem} activePanel The new active tab
28107          */
28108         "tabchange": true,
28109         /**
28110          * @event beforetabchange
28111          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28112          * @param {Roo.TabPanel} this
28113          * @param {Object} e Set cancel to true on this object to cancel the tab change
28114          * @param {Roo.TabPanelItem} tab The tab being changed to
28115          */
28116         "beforetabchange" : true
28117     });
28118
28119     Roo.EventManager.onWindowResize(this.onResize, this);
28120     this.cpad = this.el.getPadding("lr");
28121     this.hiddenCount = 0;
28122
28123
28124     // toolbar on the tabbar support...
28125     if (this.toolbar) {
28126         var tcfg = this.toolbar;
28127         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28128         this.toolbar = new Roo.Toolbar(tcfg);
28129         if (Roo.isSafari) {
28130             var tbl = tcfg.container.child('table', true);
28131             tbl.setAttribute('width', '100%');
28132         }
28133         
28134     }
28135    
28136
28137
28138     Roo.TabPanel.superclass.constructor.call(this);
28139 };
28140
28141 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28142     /*
28143      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28144      */
28145     tabPosition : "top",
28146     /*
28147      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28148      */
28149     currentTabWidth : 0,
28150     /*
28151      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28152      */
28153     minTabWidth : 40,
28154     /*
28155      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28156      */
28157     maxTabWidth : 250,
28158     /*
28159      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28160      */
28161     preferredTabWidth : 175,
28162     /*
28163      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28164      */
28165     resizeTabs : false,
28166     /*
28167      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28168      */
28169     monitorResize : true,
28170     /*
28171      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28172      */
28173     toolbar : false,
28174
28175     /**
28176      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28177      * @param {String} id The id of the div to use <b>or create</b>
28178      * @param {String} text The text for the tab
28179      * @param {String} content (optional) Content to put in the TabPanelItem body
28180      * @param {Boolean} closable (optional) True to create a close icon on the tab
28181      * @return {Roo.TabPanelItem} The created TabPanelItem
28182      */
28183     addTab : function(id, text, content, closable){
28184         var item = new Roo.TabPanelItem(this, id, text, closable);
28185         this.addTabItem(item);
28186         if(content){
28187             item.setContent(content);
28188         }
28189         return item;
28190     },
28191
28192     /**
28193      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28194      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28195      * @return {Roo.TabPanelItem}
28196      */
28197     getTab : function(id){
28198         return this.items[id];
28199     },
28200
28201     /**
28202      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28203      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28204      */
28205     hideTab : function(id){
28206         var t = this.items[id];
28207         if(!t.isHidden()){
28208            t.setHidden(true);
28209            this.hiddenCount++;
28210            this.autoSizeTabs();
28211         }
28212     },
28213
28214     /**
28215      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28216      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28217      */
28218     unhideTab : function(id){
28219         var t = this.items[id];
28220         if(t.isHidden()){
28221            t.setHidden(false);
28222            this.hiddenCount--;
28223            this.autoSizeTabs();
28224         }
28225     },
28226
28227     /**
28228      * Adds an existing {@link Roo.TabPanelItem}.
28229      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28230      */
28231     addTabItem : function(item){
28232         this.items[item.id] = item;
28233         this.items.push(item);
28234         if(this.resizeTabs){
28235            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28236            this.autoSizeTabs();
28237         }else{
28238             item.autoSize();
28239         }
28240     },
28241
28242     /**
28243      * Removes a {@link Roo.TabPanelItem}.
28244      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28245      */
28246     removeTab : function(id){
28247         var items = this.items;
28248         var tab = items[id];
28249         if(!tab) { return; }
28250         var index = items.indexOf(tab);
28251         if(this.active == tab && items.length > 1){
28252             var newTab = this.getNextAvailable(index);
28253             if(newTab) {
28254                 newTab.activate();
28255             }
28256         }
28257         this.stripEl.dom.removeChild(tab.pnode.dom);
28258         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28259             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28260         }
28261         items.splice(index, 1);
28262         delete this.items[tab.id];
28263         tab.fireEvent("close", tab);
28264         tab.purgeListeners();
28265         this.autoSizeTabs();
28266     },
28267
28268     getNextAvailable : function(start){
28269         var items = this.items;
28270         var index = start;
28271         // look for a next tab that will slide over to
28272         // replace the one being removed
28273         while(index < items.length){
28274             var item = items[++index];
28275             if(item && !item.isHidden()){
28276                 return item;
28277             }
28278         }
28279         // if one isn't found select the previous tab (on the left)
28280         index = start;
28281         while(index >= 0){
28282             var item = items[--index];
28283             if(item && !item.isHidden()){
28284                 return item;
28285             }
28286         }
28287         return null;
28288     },
28289
28290     /**
28291      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28292      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28293      */
28294     disableTab : function(id){
28295         var tab = this.items[id];
28296         if(tab && this.active != tab){
28297             tab.disable();
28298         }
28299     },
28300
28301     /**
28302      * Enables a {@link Roo.TabPanelItem} that is disabled.
28303      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28304      */
28305     enableTab : function(id){
28306         var tab = this.items[id];
28307         tab.enable();
28308     },
28309
28310     /**
28311      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28312      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28313      * @return {Roo.TabPanelItem} The TabPanelItem.
28314      */
28315     activate : function(id){
28316         var tab = this.items[id];
28317         if(!tab){
28318             return null;
28319         }
28320         if(tab == this.active || tab.disabled){
28321             return tab;
28322         }
28323         var e = {};
28324         this.fireEvent("beforetabchange", this, e, tab);
28325         if(e.cancel !== true && !tab.disabled){
28326             if(this.active){
28327                 this.active.hide();
28328             }
28329             this.active = this.items[id];
28330             this.active.show();
28331             this.fireEvent("tabchange", this, this.active);
28332         }
28333         return tab;
28334     },
28335
28336     /**
28337      * Gets the active {@link Roo.TabPanelItem}.
28338      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28339      */
28340     getActiveTab : function(){
28341         return this.active;
28342     },
28343
28344     /**
28345      * Updates the tab body element to fit the height of the container element
28346      * for overflow scrolling
28347      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28348      */
28349     syncHeight : function(targetHeight){
28350         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28351         var bm = this.bodyEl.getMargins();
28352         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28353         this.bodyEl.setHeight(newHeight);
28354         return newHeight;
28355     },
28356
28357     onResize : function(){
28358         if(this.monitorResize){
28359             this.autoSizeTabs();
28360         }
28361     },
28362
28363     /**
28364      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28365      */
28366     beginUpdate : function(){
28367         this.updating = true;
28368     },
28369
28370     /**
28371      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28372      */
28373     endUpdate : function(){
28374         this.updating = false;
28375         this.autoSizeTabs();
28376     },
28377
28378     /**
28379      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28380      */
28381     autoSizeTabs : function(){
28382         var count = this.items.length;
28383         var vcount = count - this.hiddenCount;
28384         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28385             return;
28386         }
28387         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28388         var availWidth = Math.floor(w / vcount);
28389         var b = this.stripBody;
28390         if(b.getWidth() > w){
28391             var tabs = this.items;
28392             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28393             if(availWidth < this.minTabWidth){
28394                 /*if(!this.sleft){    // incomplete scrolling code
28395                     this.createScrollButtons();
28396                 }
28397                 this.showScroll();
28398                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28399             }
28400         }else{
28401             if(this.currentTabWidth < this.preferredTabWidth){
28402                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28403             }
28404         }
28405     },
28406
28407     /**
28408      * Returns the number of tabs in this TabPanel.
28409      * @return {Number}
28410      */
28411      getCount : function(){
28412          return this.items.length;
28413      },
28414
28415     /**
28416      * Resizes all the tabs to the passed width
28417      * @param {Number} The new width
28418      */
28419     setTabWidth : function(width){
28420         this.currentTabWidth = width;
28421         for(var i = 0, len = this.items.length; i < len; i++) {
28422                 if(!this.items[i].isHidden()) {
28423                 this.items[i].setWidth(width);
28424             }
28425         }
28426     },
28427
28428     /**
28429      * Destroys this TabPanel
28430      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28431      */
28432     destroy : function(removeEl){
28433         Roo.EventManager.removeResizeListener(this.onResize, this);
28434         for(var i = 0, len = this.items.length; i < len; i++){
28435             this.items[i].purgeListeners();
28436         }
28437         if(removeEl === true){
28438             this.el.update("");
28439             this.el.remove();
28440         }
28441     }
28442 });
28443
28444 /**
28445  * @class Roo.TabPanelItem
28446  * @extends Roo.util.Observable
28447  * Represents an individual item (tab plus body) in a TabPanel.
28448  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28449  * @param {String} id The id of this TabPanelItem
28450  * @param {String} text The text for the tab of this TabPanelItem
28451  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28452  */
28453 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28454     /**
28455      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28456      * @type Roo.TabPanel
28457      */
28458     this.tabPanel = tabPanel;
28459     /**
28460      * The id for this TabPanelItem
28461      * @type String
28462      */
28463     this.id = id;
28464     /** @private */
28465     this.disabled = false;
28466     /** @private */
28467     this.text = text;
28468     /** @private */
28469     this.loaded = false;
28470     this.closable = closable;
28471
28472     /**
28473      * The body element for this TabPanelItem.
28474      * @type Roo.Element
28475      */
28476     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28477     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28478     this.bodyEl.setStyle("display", "block");
28479     this.bodyEl.setStyle("zoom", "1");
28480     this.hideAction();
28481
28482     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28483     /** @private */
28484     this.el = Roo.get(els.el, true);
28485     this.inner = Roo.get(els.inner, true);
28486     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28487     this.pnode = Roo.get(els.el.parentNode, true);
28488     this.el.on("mousedown", this.onTabMouseDown, this);
28489     this.el.on("click", this.onTabClick, this);
28490     /** @private */
28491     if(closable){
28492         var c = Roo.get(els.close, true);
28493         c.dom.title = this.closeText;
28494         c.addClassOnOver("close-over");
28495         c.on("click", this.closeClick, this);
28496      }
28497
28498     this.addEvents({
28499          /**
28500          * @event activate
28501          * Fires when this tab becomes the active tab.
28502          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28503          * @param {Roo.TabPanelItem} this
28504          */
28505         "activate": true,
28506         /**
28507          * @event beforeclose
28508          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28509          * @param {Roo.TabPanelItem} this
28510          * @param {Object} e Set cancel to true on this object to cancel the close.
28511          */
28512         "beforeclose": true,
28513         /**
28514          * @event close
28515          * Fires when this tab is closed.
28516          * @param {Roo.TabPanelItem} this
28517          */
28518          "close": true,
28519         /**
28520          * @event deactivate
28521          * Fires when this tab is no longer the active tab.
28522          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28523          * @param {Roo.TabPanelItem} this
28524          */
28525          "deactivate" : true
28526     });
28527     this.hidden = false;
28528
28529     Roo.TabPanelItem.superclass.constructor.call(this);
28530 };
28531
28532 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28533     purgeListeners : function(){
28534        Roo.util.Observable.prototype.purgeListeners.call(this);
28535        this.el.removeAllListeners();
28536     },
28537     /**
28538      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28539      */
28540     show : function(){
28541         this.pnode.addClass("on");
28542         this.showAction();
28543         if(Roo.isOpera){
28544             this.tabPanel.stripWrap.repaint();
28545         }
28546         this.fireEvent("activate", this.tabPanel, this);
28547     },
28548
28549     /**
28550      * Returns true if this tab is the active tab.
28551      * @return {Boolean}
28552      */
28553     isActive : function(){
28554         return this.tabPanel.getActiveTab() == this;
28555     },
28556
28557     /**
28558      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28559      */
28560     hide : function(){
28561         this.pnode.removeClass("on");
28562         this.hideAction();
28563         this.fireEvent("deactivate", this.tabPanel, this);
28564     },
28565
28566     hideAction : function(){
28567         this.bodyEl.hide();
28568         this.bodyEl.setStyle("position", "absolute");
28569         this.bodyEl.setLeft("-20000px");
28570         this.bodyEl.setTop("-20000px");
28571     },
28572
28573     showAction : function(){
28574         this.bodyEl.setStyle("position", "relative");
28575         this.bodyEl.setTop("");
28576         this.bodyEl.setLeft("");
28577         this.bodyEl.show();
28578     },
28579
28580     /**
28581      * Set the tooltip for the tab.
28582      * @param {String} tooltip The tab's tooltip
28583      */
28584     setTooltip : function(text){
28585         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28586             this.textEl.dom.qtip = text;
28587             this.textEl.dom.removeAttribute('title');
28588         }else{
28589             this.textEl.dom.title = text;
28590         }
28591     },
28592
28593     onTabClick : function(e){
28594         e.preventDefault();
28595         this.tabPanel.activate(this.id);
28596     },
28597
28598     onTabMouseDown : function(e){
28599         e.preventDefault();
28600         this.tabPanel.activate(this.id);
28601     },
28602
28603     getWidth : function(){
28604         return this.inner.getWidth();
28605     },
28606
28607     setWidth : function(width){
28608         var iwidth = width - this.pnode.getPadding("lr");
28609         this.inner.setWidth(iwidth);
28610         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28611         this.pnode.setWidth(width);
28612     },
28613
28614     /**
28615      * Show or hide the tab
28616      * @param {Boolean} hidden True to hide or false to show.
28617      */
28618     setHidden : function(hidden){
28619         this.hidden = hidden;
28620         this.pnode.setStyle("display", hidden ? "none" : "");
28621     },
28622
28623     /**
28624      * Returns true if this tab is "hidden"
28625      * @return {Boolean}
28626      */
28627     isHidden : function(){
28628         return this.hidden;
28629     },
28630
28631     /**
28632      * Returns the text for this tab
28633      * @return {String}
28634      */
28635     getText : function(){
28636         return this.text;
28637     },
28638
28639     autoSize : function(){
28640         //this.el.beginMeasure();
28641         this.textEl.setWidth(1);
28642         /*
28643          *  #2804 [new] Tabs in Roojs
28644          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28645          */
28646         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28647         //this.el.endMeasure();
28648     },
28649
28650     /**
28651      * Sets the text for the tab (Note: this also sets the tooltip text)
28652      * @param {String} text The tab's text and tooltip
28653      */
28654     setText : function(text){
28655         this.text = text;
28656         this.textEl.update(text);
28657         this.setTooltip(text);
28658         if(!this.tabPanel.resizeTabs){
28659             this.autoSize();
28660         }
28661     },
28662     /**
28663      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28664      */
28665     activate : function(){
28666         this.tabPanel.activate(this.id);
28667     },
28668
28669     /**
28670      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28671      */
28672     disable : function(){
28673         if(this.tabPanel.active != this){
28674             this.disabled = true;
28675             this.pnode.addClass("disabled");
28676         }
28677     },
28678
28679     /**
28680      * Enables this TabPanelItem if it was previously disabled.
28681      */
28682     enable : function(){
28683         this.disabled = false;
28684         this.pnode.removeClass("disabled");
28685     },
28686
28687     /**
28688      * Sets the content for this TabPanelItem.
28689      * @param {String} content The content
28690      * @param {Boolean} loadScripts true to look for and load scripts
28691      */
28692     setContent : function(content, loadScripts){
28693         this.bodyEl.update(content, loadScripts);
28694     },
28695
28696     /**
28697      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28698      * @return {Roo.UpdateManager} The UpdateManager
28699      */
28700     getUpdateManager : function(){
28701         return this.bodyEl.getUpdateManager();
28702     },
28703
28704     /**
28705      * Set a URL to be used to load the content for this TabPanelItem.
28706      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28707      * @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)
28708      * @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)
28709      * @return {Roo.UpdateManager} The UpdateManager
28710      */
28711     setUrl : function(url, params, loadOnce){
28712         if(this.refreshDelegate){
28713             this.un('activate', this.refreshDelegate);
28714         }
28715         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28716         this.on("activate", this.refreshDelegate);
28717         return this.bodyEl.getUpdateManager();
28718     },
28719
28720     /** @private */
28721     _handleRefresh : function(url, params, loadOnce){
28722         if(!loadOnce || !this.loaded){
28723             var updater = this.bodyEl.getUpdateManager();
28724             updater.update(url, params, this._setLoaded.createDelegate(this));
28725         }
28726     },
28727
28728     /**
28729      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28730      *   Will fail silently if the setUrl method has not been called.
28731      *   This does not activate the panel, just updates its content.
28732      */
28733     refresh : function(){
28734         if(this.refreshDelegate){
28735            this.loaded = false;
28736            this.refreshDelegate();
28737         }
28738     },
28739
28740     /** @private */
28741     _setLoaded : function(){
28742         this.loaded = true;
28743     },
28744
28745     /** @private */
28746     closeClick : function(e){
28747         var o = {};
28748         e.stopEvent();
28749         this.fireEvent("beforeclose", this, o);
28750         if(o.cancel !== true){
28751             this.tabPanel.removeTab(this.id);
28752         }
28753     },
28754     /**
28755      * The text displayed in the tooltip for the close icon.
28756      * @type String
28757      */
28758     closeText : "Close this tab"
28759 });
28760
28761 /** @private */
28762 Roo.TabPanel.prototype.createStrip = function(container){
28763     var strip = document.createElement("div");
28764     strip.className = "x-tabs-wrap";
28765     container.appendChild(strip);
28766     return strip;
28767 };
28768 /** @private */
28769 Roo.TabPanel.prototype.createStripList = function(strip){
28770     // div wrapper for retard IE
28771     // returns the "tr" element.
28772     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28773         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28774         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28775     return strip.firstChild.firstChild.firstChild.firstChild;
28776 };
28777 /** @private */
28778 Roo.TabPanel.prototype.createBody = function(container){
28779     var body = document.createElement("div");
28780     Roo.id(body, "tab-body");
28781     Roo.fly(body).addClass("x-tabs-body");
28782     container.appendChild(body);
28783     return body;
28784 };
28785 /** @private */
28786 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28787     var body = Roo.getDom(id);
28788     if(!body){
28789         body = document.createElement("div");
28790         body.id = id;
28791     }
28792     Roo.fly(body).addClass("x-tabs-item-body");
28793     bodyEl.insertBefore(body, bodyEl.firstChild);
28794     return body;
28795 };
28796 /** @private */
28797 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28798     var td = document.createElement("td");
28799     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28800     //stripEl.appendChild(td);
28801     if(closable){
28802         td.className = "x-tabs-closable";
28803         if(!this.closeTpl){
28804             this.closeTpl = new Roo.Template(
28805                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28806                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28807                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28808             );
28809         }
28810         var el = this.closeTpl.overwrite(td, {"text": text});
28811         var close = el.getElementsByTagName("div")[0];
28812         var inner = el.getElementsByTagName("em")[0];
28813         return {"el": el, "close": close, "inner": inner};
28814     } else {
28815         if(!this.tabTpl){
28816             this.tabTpl = new Roo.Template(
28817                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28818                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28819             );
28820         }
28821         var el = this.tabTpl.overwrite(td, {"text": text});
28822         var inner = el.getElementsByTagName("em")[0];
28823         return {"el": el, "inner": inner};
28824     }
28825 };/*
28826  * Based on:
28827  * Ext JS Library 1.1.1
28828  * Copyright(c) 2006-2007, Ext JS, LLC.
28829  *
28830  * Originally Released Under LGPL - original licence link has changed is not relivant.
28831  *
28832  * Fork - LGPL
28833  * <script type="text/javascript">
28834  */
28835
28836 /**
28837  * @class Roo.Button
28838  * @extends Roo.util.Observable
28839  * Simple Button class
28840  * @cfg {String} text The button text
28841  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28842  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28843  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28844  * @cfg {Object} scope The scope of the handler
28845  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28846  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28847  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28848  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28849  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28850  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28851    applies if enableToggle = true)
28852  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28853  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28854   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28855  * @constructor
28856  * Create a new button
28857  * @param {Object} config The config object
28858  */
28859 Roo.Button = function(renderTo, config)
28860 {
28861     if (!config) {
28862         config = renderTo;
28863         renderTo = config.renderTo || false;
28864     }
28865     
28866     Roo.apply(this, config);
28867     this.addEvents({
28868         /**
28869              * @event click
28870              * Fires when this button is clicked
28871              * @param {Button} this
28872              * @param {EventObject} e The click event
28873              */
28874             "click" : true,
28875         /**
28876              * @event toggle
28877              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
28878              * @param {Button} this
28879              * @param {Boolean} pressed
28880              */
28881             "toggle" : true,
28882         /**
28883              * @event mouseover
28884              * Fires when the mouse hovers over the button
28885              * @param {Button} this
28886              * @param {Event} e The event object
28887              */
28888         'mouseover' : true,
28889         /**
28890              * @event mouseout
28891              * Fires when the mouse exits the button
28892              * @param {Button} this
28893              * @param {Event} e The event object
28894              */
28895         'mouseout': true,
28896          /**
28897              * @event render
28898              * Fires when the button is rendered
28899              * @param {Button} this
28900              */
28901         'render': true
28902     });
28903     if(this.menu){
28904         this.menu = Roo.menu.MenuMgr.get(this.menu);
28905     }
28906     // register listeners first!!  - so render can be captured..
28907     Roo.util.Observable.call(this);
28908     if(renderTo){
28909         this.render(renderTo);
28910     }
28911     
28912   
28913 };
28914
28915 Roo.extend(Roo.Button, Roo.util.Observable, {
28916     /**
28917      * 
28918      */
28919     
28920     /**
28921      * Read-only. True if this button is hidden
28922      * @type Boolean
28923      */
28924     hidden : false,
28925     /**
28926      * Read-only. True if this button is disabled
28927      * @type Boolean
28928      */
28929     disabled : false,
28930     /**
28931      * Read-only. True if this button is pressed (only if enableToggle = true)
28932      * @type Boolean
28933      */
28934     pressed : false,
28935
28936     /**
28937      * @cfg {Number} tabIndex 
28938      * The DOM tabIndex for this button (defaults to undefined)
28939      */
28940     tabIndex : undefined,
28941
28942     /**
28943      * @cfg {Boolean} enableToggle
28944      * True to enable pressed/not pressed toggling (defaults to false)
28945      */
28946     enableToggle: false,
28947     /**
28948      * @cfg {Mixed} menu
28949      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
28950      */
28951     menu : undefined,
28952     /**
28953      * @cfg {String} menuAlign
28954      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
28955      */
28956     menuAlign : "tl-bl?",
28957
28958     /**
28959      * @cfg {String} iconCls
28960      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
28961      */
28962     iconCls : undefined,
28963     /**
28964      * @cfg {String} type
28965      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
28966      */
28967     type : 'button',
28968
28969     // private
28970     menuClassTarget: 'tr',
28971
28972     /**
28973      * @cfg {String} clickEvent
28974      * The type of event to map to the button's event handler (defaults to 'click')
28975      */
28976     clickEvent : 'click',
28977
28978     /**
28979      * @cfg {Boolean} handleMouseEvents
28980      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
28981      */
28982     handleMouseEvents : true,
28983
28984     /**
28985      * @cfg {String} tooltipType
28986      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
28987      */
28988     tooltipType : 'qtip',
28989
28990     /**
28991      * @cfg {String} cls
28992      * A CSS class to apply to the button's main element.
28993      */
28994     
28995     /**
28996      * @cfg {Roo.Template} template (Optional)
28997      * An {@link Roo.Template} with which to create the Button's main element. This Template must
28998      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
28999      * require code modifications if required elements (e.g. a button) aren't present.
29000      */
29001
29002     // private
29003     render : function(renderTo){
29004         var btn;
29005         if(this.hideParent){
29006             this.parentEl = Roo.get(renderTo);
29007         }
29008         if(!this.dhconfig){
29009             if(!this.template){
29010                 if(!Roo.Button.buttonTemplate){
29011                     // hideous table template
29012                     Roo.Button.buttonTemplate = new Roo.Template(
29013                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29014                         '<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>',
29015                         "</tr></tbody></table>");
29016                 }
29017                 this.template = Roo.Button.buttonTemplate;
29018             }
29019             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29020             var btnEl = btn.child("button:first");
29021             btnEl.on('focus', this.onFocus, this);
29022             btnEl.on('blur', this.onBlur, this);
29023             if(this.cls){
29024                 btn.addClass(this.cls);
29025             }
29026             if(this.icon){
29027                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29028             }
29029             if(this.iconCls){
29030                 btnEl.addClass(this.iconCls);
29031                 if(!this.cls){
29032                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29033                 }
29034             }
29035             if(this.tabIndex !== undefined){
29036                 btnEl.dom.tabIndex = this.tabIndex;
29037             }
29038             if(this.tooltip){
29039                 if(typeof this.tooltip == 'object'){
29040                     Roo.QuickTips.tips(Roo.apply({
29041                           target: btnEl.id
29042                     }, this.tooltip));
29043                 } else {
29044                     btnEl.dom[this.tooltipType] = this.tooltip;
29045                 }
29046             }
29047         }else{
29048             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29049         }
29050         this.el = btn;
29051         if(this.id){
29052             this.el.dom.id = this.el.id = this.id;
29053         }
29054         if(this.menu){
29055             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29056             this.menu.on("show", this.onMenuShow, this);
29057             this.menu.on("hide", this.onMenuHide, this);
29058         }
29059         btn.addClass("x-btn");
29060         if(Roo.isIE && !Roo.isIE7){
29061             this.autoWidth.defer(1, this);
29062         }else{
29063             this.autoWidth();
29064         }
29065         if(this.handleMouseEvents){
29066             btn.on("mouseover", this.onMouseOver, this);
29067             btn.on("mouseout", this.onMouseOut, this);
29068             btn.on("mousedown", this.onMouseDown, this);
29069         }
29070         btn.on(this.clickEvent, this.onClick, this);
29071         //btn.on("mouseup", this.onMouseUp, this);
29072         if(this.hidden){
29073             this.hide();
29074         }
29075         if(this.disabled){
29076             this.disable();
29077         }
29078         Roo.ButtonToggleMgr.register(this);
29079         if(this.pressed){
29080             this.el.addClass("x-btn-pressed");
29081         }
29082         if(this.repeat){
29083             var repeater = new Roo.util.ClickRepeater(btn,
29084                 typeof this.repeat == "object" ? this.repeat : {}
29085             );
29086             repeater.on("click", this.onClick,  this);
29087         }
29088         
29089         this.fireEvent('render', this);
29090         
29091     },
29092     /**
29093      * Returns the button's underlying element
29094      * @return {Roo.Element} The element
29095      */
29096     getEl : function(){
29097         return this.el;  
29098     },
29099     
29100     /**
29101      * Destroys this Button and removes any listeners.
29102      */
29103     destroy : function(){
29104         Roo.ButtonToggleMgr.unregister(this);
29105         this.el.removeAllListeners();
29106         this.purgeListeners();
29107         this.el.remove();
29108     },
29109
29110     // private
29111     autoWidth : function(){
29112         if(this.el){
29113             this.el.setWidth("auto");
29114             if(Roo.isIE7 && Roo.isStrict){
29115                 var ib = this.el.child('button');
29116                 if(ib && ib.getWidth() > 20){
29117                     ib.clip();
29118                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29119                 }
29120             }
29121             if(this.minWidth){
29122                 if(this.hidden){
29123                     this.el.beginMeasure();
29124                 }
29125                 if(this.el.getWidth() < this.minWidth){
29126                     this.el.setWidth(this.minWidth);
29127                 }
29128                 if(this.hidden){
29129                     this.el.endMeasure();
29130                 }
29131             }
29132         }
29133     },
29134
29135     /**
29136      * Assigns this button's click handler
29137      * @param {Function} handler The function to call when the button is clicked
29138      * @param {Object} scope (optional) Scope for the function passed in
29139      */
29140     setHandler : function(handler, scope){
29141         this.handler = handler;
29142         this.scope = scope;  
29143     },
29144     
29145     /**
29146      * Sets this button's text
29147      * @param {String} text The button text
29148      */
29149     setText : function(text){
29150         this.text = text;
29151         if(this.el){
29152             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29153         }
29154         this.autoWidth();
29155     },
29156     
29157     /**
29158      * Gets the text for this button
29159      * @return {String} The button text
29160      */
29161     getText : function(){
29162         return this.text;  
29163     },
29164     
29165     /**
29166      * Show this button
29167      */
29168     show: function(){
29169         this.hidden = false;
29170         if(this.el){
29171             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29172         }
29173     },
29174     
29175     /**
29176      * Hide this button
29177      */
29178     hide: function(){
29179         this.hidden = true;
29180         if(this.el){
29181             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29182         }
29183     },
29184     
29185     /**
29186      * Convenience function for boolean show/hide
29187      * @param {Boolean} visible True to show, false to hide
29188      */
29189     setVisible: function(visible){
29190         if(visible) {
29191             this.show();
29192         }else{
29193             this.hide();
29194         }
29195     },
29196     
29197     /**
29198      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29199      * @param {Boolean} state (optional) Force a particular state
29200      */
29201     toggle : function(state){
29202         state = state === undefined ? !this.pressed : state;
29203         if(state != this.pressed){
29204             if(state){
29205                 this.el.addClass("x-btn-pressed");
29206                 this.pressed = true;
29207                 this.fireEvent("toggle", this, true);
29208             }else{
29209                 this.el.removeClass("x-btn-pressed");
29210                 this.pressed = false;
29211                 this.fireEvent("toggle", this, false);
29212             }
29213             if(this.toggleHandler){
29214                 this.toggleHandler.call(this.scope || this, this, state);
29215             }
29216         }
29217     },
29218     
29219     /**
29220      * Focus the button
29221      */
29222     focus : function(){
29223         this.el.child('button:first').focus();
29224     },
29225     
29226     /**
29227      * Disable this button
29228      */
29229     disable : function(){
29230         if(this.el){
29231             this.el.addClass("x-btn-disabled");
29232         }
29233         this.disabled = true;
29234     },
29235     
29236     /**
29237      * Enable this button
29238      */
29239     enable : function(){
29240         if(this.el){
29241             this.el.removeClass("x-btn-disabled");
29242         }
29243         this.disabled = false;
29244     },
29245
29246     /**
29247      * Convenience function for boolean enable/disable
29248      * @param {Boolean} enabled True to enable, false to disable
29249      */
29250     setDisabled : function(v){
29251         this[v !== true ? "enable" : "disable"]();
29252     },
29253
29254     // private
29255     onClick : function(e)
29256     {
29257         if(e){
29258             e.preventDefault();
29259         }
29260         if(e.button != 0){
29261             return;
29262         }
29263         if(!this.disabled){
29264             if(this.enableToggle){
29265                 this.toggle();
29266             }
29267             if(this.menu && !this.menu.isVisible()){
29268                 this.menu.show(this.el, this.menuAlign);
29269             }
29270             this.fireEvent("click", this, e);
29271             if(this.handler){
29272                 this.el.removeClass("x-btn-over");
29273                 this.handler.call(this.scope || this, this, e);
29274             }
29275         }
29276     },
29277     // private
29278     onMouseOver : function(e){
29279         if(!this.disabled){
29280             this.el.addClass("x-btn-over");
29281             this.fireEvent('mouseover', this, e);
29282         }
29283     },
29284     // private
29285     onMouseOut : function(e){
29286         if(!e.within(this.el,  true)){
29287             this.el.removeClass("x-btn-over");
29288             this.fireEvent('mouseout', this, e);
29289         }
29290     },
29291     // private
29292     onFocus : function(e){
29293         if(!this.disabled){
29294             this.el.addClass("x-btn-focus");
29295         }
29296     },
29297     // private
29298     onBlur : function(e){
29299         this.el.removeClass("x-btn-focus");
29300     },
29301     // private
29302     onMouseDown : function(e){
29303         if(!this.disabled && e.button == 0){
29304             this.el.addClass("x-btn-click");
29305             Roo.get(document).on('mouseup', this.onMouseUp, this);
29306         }
29307     },
29308     // private
29309     onMouseUp : function(e){
29310         if(e.button == 0){
29311             this.el.removeClass("x-btn-click");
29312             Roo.get(document).un('mouseup', this.onMouseUp, this);
29313         }
29314     },
29315     // private
29316     onMenuShow : function(e){
29317         this.el.addClass("x-btn-menu-active");
29318     },
29319     // private
29320     onMenuHide : function(e){
29321         this.el.removeClass("x-btn-menu-active");
29322     }   
29323 });
29324
29325 // Private utility class used by Button
29326 Roo.ButtonToggleMgr = function(){
29327    var groups = {};
29328    
29329    function toggleGroup(btn, state){
29330        if(state){
29331            var g = groups[btn.toggleGroup];
29332            for(var i = 0, l = g.length; i < l; i++){
29333                if(g[i] != btn){
29334                    g[i].toggle(false);
29335                }
29336            }
29337        }
29338    }
29339    
29340    return {
29341        register : function(btn){
29342            if(!btn.toggleGroup){
29343                return;
29344            }
29345            var g = groups[btn.toggleGroup];
29346            if(!g){
29347                g = groups[btn.toggleGroup] = [];
29348            }
29349            g.push(btn);
29350            btn.on("toggle", toggleGroup);
29351        },
29352        
29353        unregister : function(btn){
29354            if(!btn.toggleGroup){
29355                return;
29356            }
29357            var g = groups[btn.toggleGroup];
29358            if(g){
29359                g.remove(btn);
29360                btn.un("toggle", toggleGroup);
29361            }
29362        }
29363    };
29364 }();/*
29365  * Based on:
29366  * Ext JS Library 1.1.1
29367  * Copyright(c) 2006-2007, Ext JS, LLC.
29368  *
29369  * Originally Released Under LGPL - original licence link has changed is not relivant.
29370  *
29371  * Fork - LGPL
29372  * <script type="text/javascript">
29373  */
29374  
29375 /**
29376  * @class Roo.SplitButton
29377  * @extends Roo.Button
29378  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29379  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29380  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29381  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29382  * @cfg {String} arrowTooltip The title attribute of the arrow
29383  * @constructor
29384  * Create a new menu button
29385  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29386  * @param {Object} config The config object
29387  */
29388 Roo.SplitButton = function(renderTo, config){
29389     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29390     /**
29391      * @event arrowclick
29392      * Fires when this button's arrow is clicked
29393      * @param {SplitButton} this
29394      * @param {EventObject} e The click event
29395      */
29396     this.addEvents({"arrowclick":true});
29397 };
29398
29399 Roo.extend(Roo.SplitButton, Roo.Button, {
29400     render : function(renderTo){
29401         // this is one sweet looking template!
29402         var tpl = new Roo.Template(
29403             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29404             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29405             '<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>',
29406             "</tbody></table></td><td>",
29407             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29408             '<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>',
29409             "</tbody></table></td></tr></table>"
29410         );
29411         var btn = tpl.append(renderTo, [this.text, this.type], true);
29412         var btnEl = btn.child("button");
29413         if(this.cls){
29414             btn.addClass(this.cls);
29415         }
29416         if(this.icon){
29417             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29418         }
29419         if(this.iconCls){
29420             btnEl.addClass(this.iconCls);
29421             if(!this.cls){
29422                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29423             }
29424         }
29425         this.el = btn;
29426         if(this.handleMouseEvents){
29427             btn.on("mouseover", this.onMouseOver, this);
29428             btn.on("mouseout", this.onMouseOut, this);
29429             btn.on("mousedown", this.onMouseDown, this);
29430             btn.on("mouseup", this.onMouseUp, this);
29431         }
29432         btn.on(this.clickEvent, this.onClick, this);
29433         if(this.tooltip){
29434             if(typeof this.tooltip == 'object'){
29435                 Roo.QuickTips.tips(Roo.apply({
29436                       target: btnEl.id
29437                 }, this.tooltip));
29438             } else {
29439                 btnEl.dom[this.tooltipType] = this.tooltip;
29440             }
29441         }
29442         if(this.arrowTooltip){
29443             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29444         }
29445         if(this.hidden){
29446             this.hide();
29447         }
29448         if(this.disabled){
29449             this.disable();
29450         }
29451         if(this.pressed){
29452             this.el.addClass("x-btn-pressed");
29453         }
29454         if(Roo.isIE && !Roo.isIE7){
29455             this.autoWidth.defer(1, this);
29456         }else{
29457             this.autoWidth();
29458         }
29459         if(this.menu){
29460             this.menu.on("show", this.onMenuShow, this);
29461             this.menu.on("hide", this.onMenuHide, this);
29462         }
29463         this.fireEvent('render', this);
29464     },
29465
29466     // private
29467     autoWidth : function(){
29468         if(this.el){
29469             var tbl = this.el.child("table:first");
29470             var tbl2 = this.el.child("table:last");
29471             this.el.setWidth("auto");
29472             tbl.setWidth("auto");
29473             if(Roo.isIE7 && Roo.isStrict){
29474                 var ib = this.el.child('button:first');
29475                 if(ib && ib.getWidth() > 20){
29476                     ib.clip();
29477                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29478                 }
29479             }
29480             if(this.minWidth){
29481                 if(this.hidden){
29482                     this.el.beginMeasure();
29483                 }
29484                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29485                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29486                 }
29487                 if(this.hidden){
29488                     this.el.endMeasure();
29489                 }
29490             }
29491             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29492         } 
29493     },
29494     /**
29495      * Sets this button's click handler
29496      * @param {Function} handler The function to call when the button is clicked
29497      * @param {Object} scope (optional) Scope for the function passed above
29498      */
29499     setHandler : function(handler, scope){
29500         this.handler = handler;
29501         this.scope = scope;  
29502     },
29503     
29504     /**
29505      * Sets this button's arrow click handler
29506      * @param {Function} handler The function to call when the arrow is clicked
29507      * @param {Object} scope (optional) Scope for the function passed above
29508      */
29509     setArrowHandler : function(handler, scope){
29510         this.arrowHandler = handler;
29511         this.scope = scope;  
29512     },
29513     
29514     /**
29515      * Focus the button
29516      */
29517     focus : function(){
29518         if(this.el){
29519             this.el.child("button:first").focus();
29520         }
29521     },
29522
29523     // private
29524     onClick : function(e){
29525         e.preventDefault();
29526         if(!this.disabled){
29527             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29528                 if(this.menu && !this.menu.isVisible()){
29529                     this.menu.show(this.el, this.menuAlign);
29530                 }
29531                 this.fireEvent("arrowclick", this, e);
29532                 if(this.arrowHandler){
29533                     this.arrowHandler.call(this.scope || this, this, e);
29534                 }
29535             }else{
29536                 this.fireEvent("click", this, e);
29537                 if(this.handler){
29538                     this.handler.call(this.scope || this, this, e);
29539                 }
29540             }
29541         }
29542     },
29543     // private
29544     onMouseDown : function(e){
29545         if(!this.disabled){
29546             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29547         }
29548     },
29549     // private
29550     onMouseUp : function(e){
29551         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29552     }   
29553 });
29554
29555
29556 // backwards compat
29557 Roo.MenuButton = Roo.SplitButton;/*
29558  * Based on:
29559  * Ext JS Library 1.1.1
29560  * Copyright(c) 2006-2007, Ext JS, LLC.
29561  *
29562  * Originally Released Under LGPL - original licence link has changed is not relivant.
29563  *
29564  * Fork - LGPL
29565  * <script type="text/javascript">
29566  */
29567
29568 /**
29569  * @class Roo.Toolbar
29570  * Basic Toolbar class.
29571  * @constructor
29572  * Creates a new Toolbar
29573  * @param {Object} container The config object
29574  */ 
29575 Roo.Toolbar = function(container, buttons, config)
29576 {
29577     /// old consturctor format still supported..
29578     if(container instanceof Array){ // omit the container for later rendering
29579         buttons = container;
29580         config = buttons;
29581         container = null;
29582     }
29583     if (typeof(container) == 'object' && container.xtype) {
29584         config = container;
29585         container = config.container;
29586         buttons = config.buttons || []; // not really - use items!!
29587     }
29588     var xitems = [];
29589     if (config && config.items) {
29590         xitems = config.items;
29591         delete config.items;
29592     }
29593     Roo.apply(this, config);
29594     this.buttons = buttons;
29595     
29596     if(container){
29597         this.render(container);
29598     }
29599     this.xitems = xitems;
29600     Roo.each(xitems, function(b) {
29601         this.add(b);
29602     }, this);
29603     
29604 };
29605
29606 Roo.Toolbar.prototype = {
29607     /**
29608      * @cfg {Array} items
29609      * array of button configs or elements to add (will be converted to a MixedCollection)
29610      */
29611     
29612     /**
29613      * @cfg {String/HTMLElement/Element} container
29614      * The id or element that will contain the toolbar
29615      */
29616     // private
29617     render : function(ct){
29618         this.el = Roo.get(ct);
29619         if(this.cls){
29620             this.el.addClass(this.cls);
29621         }
29622         // using a table allows for vertical alignment
29623         // 100% width is needed by Safari...
29624         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29625         this.tr = this.el.child("tr", true);
29626         var autoId = 0;
29627         this.items = new Roo.util.MixedCollection(false, function(o){
29628             return o.id || ("item" + (++autoId));
29629         });
29630         if(this.buttons){
29631             this.add.apply(this, this.buttons);
29632             delete this.buttons;
29633         }
29634     },
29635
29636     /**
29637      * Adds element(s) to the toolbar -- this function takes a variable number of 
29638      * arguments of mixed type and adds them to the toolbar.
29639      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29640      * <ul>
29641      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29642      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29643      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29644      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29645      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29646      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29647      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29648      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29649      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29650      * </ul>
29651      * @param {Mixed} arg2
29652      * @param {Mixed} etc.
29653      */
29654     add : function(){
29655         var a = arguments, l = a.length;
29656         for(var i = 0; i < l; i++){
29657             this._add(a[i]);
29658         }
29659     },
29660     // private..
29661     _add : function(el) {
29662         
29663         if (el.xtype) {
29664             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29665         }
29666         
29667         if (el.applyTo){ // some kind of form field
29668             return this.addField(el);
29669         } 
29670         if (el.render){ // some kind of Toolbar.Item
29671             return this.addItem(el);
29672         }
29673         if (typeof el == "string"){ // string
29674             if(el == "separator" || el == "-"){
29675                 return this.addSeparator();
29676             }
29677             if (el == " "){
29678                 return this.addSpacer();
29679             }
29680             if(el == "->"){
29681                 return this.addFill();
29682             }
29683             return this.addText(el);
29684             
29685         }
29686         if(el.tagName){ // element
29687             return this.addElement(el);
29688         }
29689         if(typeof el == "object"){ // must be button config?
29690             return this.addButton(el);
29691         }
29692         // and now what?!?!
29693         return false;
29694         
29695     },
29696     
29697     /**
29698      * Add an Xtype element
29699      * @param {Object} xtype Xtype Object
29700      * @return {Object} created Object
29701      */
29702     addxtype : function(e){
29703         return this.add(e);  
29704     },
29705     
29706     /**
29707      * Returns the Element for this toolbar.
29708      * @return {Roo.Element}
29709      */
29710     getEl : function(){
29711         return this.el;  
29712     },
29713     
29714     /**
29715      * Adds a separator
29716      * @return {Roo.Toolbar.Item} The separator item
29717      */
29718     addSeparator : function(){
29719         return this.addItem(new Roo.Toolbar.Separator());
29720     },
29721
29722     /**
29723      * Adds a spacer element
29724      * @return {Roo.Toolbar.Spacer} The spacer item
29725      */
29726     addSpacer : function(){
29727         return this.addItem(new Roo.Toolbar.Spacer());
29728     },
29729
29730     /**
29731      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29732      * @return {Roo.Toolbar.Fill} The fill item
29733      */
29734     addFill : function(){
29735         return this.addItem(new Roo.Toolbar.Fill());
29736     },
29737
29738     /**
29739      * Adds any standard HTML element to the toolbar
29740      * @param {String/HTMLElement/Element} el The element or id of the element to add
29741      * @return {Roo.Toolbar.Item} The element's item
29742      */
29743     addElement : function(el){
29744         return this.addItem(new Roo.Toolbar.Item(el));
29745     },
29746     /**
29747      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29748      * @type Roo.util.MixedCollection  
29749      */
29750     items : false,
29751      
29752     /**
29753      * Adds any Toolbar.Item or subclass
29754      * @param {Roo.Toolbar.Item} item
29755      * @return {Roo.Toolbar.Item} The item
29756      */
29757     addItem : function(item){
29758         var td = this.nextBlock();
29759         item.render(td);
29760         this.items.add(item);
29761         return item;
29762     },
29763     
29764     /**
29765      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29766      * @param {Object/Array} config A button config or array of configs
29767      * @return {Roo.Toolbar.Button/Array}
29768      */
29769     addButton : function(config){
29770         if(config instanceof Array){
29771             var buttons = [];
29772             for(var i = 0, len = config.length; i < len; i++) {
29773                 buttons.push(this.addButton(config[i]));
29774             }
29775             return buttons;
29776         }
29777         var b = config;
29778         if(!(config instanceof Roo.Toolbar.Button)){
29779             b = config.split ?
29780                 new Roo.Toolbar.SplitButton(config) :
29781                 new Roo.Toolbar.Button(config);
29782         }
29783         var td = this.nextBlock();
29784         b.render(td);
29785         this.items.add(b);
29786         return b;
29787     },
29788     
29789     /**
29790      * Adds text to the toolbar
29791      * @param {String} text The text to add
29792      * @return {Roo.Toolbar.Item} The element's item
29793      */
29794     addText : function(text){
29795         return this.addItem(new Roo.Toolbar.TextItem(text));
29796     },
29797     
29798     /**
29799      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29800      * @param {Number} index The index where the item is to be inserted
29801      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29802      * @return {Roo.Toolbar.Button/Item}
29803      */
29804     insertButton : function(index, item){
29805         if(item instanceof Array){
29806             var buttons = [];
29807             for(var i = 0, len = item.length; i < len; i++) {
29808                buttons.push(this.insertButton(index + i, item[i]));
29809             }
29810             return buttons;
29811         }
29812         if (!(item instanceof Roo.Toolbar.Button)){
29813            item = new Roo.Toolbar.Button(item);
29814         }
29815         var td = document.createElement("td");
29816         this.tr.insertBefore(td, this.tr.childNodes[index]);
29817         item.render(td);
29818         this.items.insert(index, item);
29819         return item;
29820     },
29821     
29822     /**
29823      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29824      * @param {Object} config
29825      * @return {Roo.Toolbar.Item} The element's item
29826      */
29827     addDom : function(config, returnEl){
29828         var td = this.nextBlock();
29829         Roo.DomHelper.overwrite(td, config);
29830         var ti = new Roo.Toolbar.Item(td.firstChild);
29831         ti.render(td);
29832         this.items.add(ti);
29833         return ti;
29834     },
29835
29836     /**
29837      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29838      * @type Roo.util.MixedCollection  
29839      */
29840     fields : false,
29841     
29842     /**
29843      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29844      * Note: the field should not have been rendered yet. For a field that has already been
29845      * rendered, use {@link #addElement}.
29846      * @param {Roo.form.Field} field
29847      * @return {Roo.ToolbarItem}
29848      */
29849      
29850       
29851     addField : function(field) {
29852         if (!this.fields) {
29853             var autoId = 0;
29854             this.fields = new Roo.util.MixedCollection(false, function(o){
29855                 return o.id || ("item" + (++autoId));
29856             });
29857
29858         }
29859         
29860         var td = this.nextBlock();
29861         field.render(td);
29862         var ti = new Roo.Toolbar.Item(td.firstChild);
29863         ti.render(td);
29864         this.items.add(ti);
29865         this.fields.add(field);
29866         return ti;
29867     },
29868     /**
29869      * Hide the toolbar
29870      * @method hide
29871      */
29872      
29873       
29874     hide : function()
29875     {
29876         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
29877         this.el.child('div').hide();
29878     },
29879     /**
29880      * Show the toolbar
29881      * @method show
29882      */
29883     show : function()
29884     {
29885         this.el.child('div').show();
29886     },
29887       
29888     // private
29889     nextBlock : function(){
29890         var td = document.createElement("td");
29891         this.tr.appendChild(td);
29892         return td;
29893     },
29894
29895     // private
29896     destroy : function(){
29897         if(this.items){ // rendered?
29898             Roo.destroy.apply(Roo, this.items.items);
29899         }
29900         if(this.fields){ // rendered?
29901             Roo.destroy.apply(Roo, this.fields.items);
29902         }
29903         Roo.Element.uncache(this.el, this.tr);
29904     }
29905 };
29906
29907 /**
29908  * @class Roo.Toolbar.Item
29909  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
29910  * @constructor
29911  * Creates a new Item
29912  * @param {HTMLElement} el 
29913  */
29914 Roo.Toolbar.Item = function(el){
29915     var cfg = {};
29916     if (typeof (el.xtype) != 'undefined') {
29917         cfg = el;
29918         el = cfg.el;
29919     }
29920     
29921     this.el = Roo.getDom(el);
29922     this.id = Roo.id(this.el);
29923     this.hidden = false;
29924     
29925     this.addEvents({
29926          /**
29927              * @event render
29928              * Fires when the button is rendered
29929              * @param {Button} this
29930              */
29931         'render': true
29932     });
29933     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
29934 };
29935 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
29936 //Roo.Toolbar.Item.prototype = {
29937     
29938     /**
29939      * Get this item's HTML Element
29940      * @return {HTMLElement}
29941      */
29942     getEl : function(){
29943        return this.el;  
29944     },
29945
29946     // private
29947     render : function(td){
29948         
29949          this.td = td;
29950         td.appendChild(this.el);
29951         
29952         this.fireEvent('render', this);
29953     },
29954     
29955     /**
29956      * Removes and destroys this item.
29957      */
29958     destroy : function(){
29959         this.td.parentNode.removeChild(this.td);
29960     },
29961     
29962     /**
29963      * Shows this item.
29964      */
29965     show: function(){
29966         this.hidden = false;
29967         this.td.style.display = "";
29968     },
29969     
29970     /**
29971      * Hides this item.
29972      */
29973     hide: function(){
29974         this.hidden = true;
29975         this.td.style.display = "none";
29976     },
29977     
29978     /**
29979      * Convenience function for boolean show/hide.
29980      * @param {Boolean} visible true to show/false to hide
29981      */
29982     setVisible: function(visible){
29983         if(visible) {
29984             this.show();
29985         }else{
29986             this.hide();
29987         }
29988     },
29989     
29990     /**
29991      * Try to focus this item.
29992      */
29993     focus : function(){
29994         Roo.fly(this.el).focus();
29995     },
29996     
29997     /**
29998      * Disables this item.
29999      */
30000     disable : function(){
30001         Roo.fly(this.td).addClass("x-item-disabled");
30002         this.disabled = true;
30003         this.el.disabled = true;
30004     },
30005     
30006     /**
30007      * Enables this item.
30008      */
30009     enable : function(){
30010         Roo.fly(this.td).removeClass("x-item-disabled");
30011         this.disabled = false;
30012         this.el.disabled = false;
30013     }
30014 });
30015
30016
30017 /**
30018  * @class Roo.Toolbar.Separator
30019  * @extends Roo.Toolbar.Item
30020  * A simple toolbar separator class
30021  * @constructor
30022  * Creates a new Separator
30023  */
30024 Roo.Toolbar.Separator = function(cfg){
30025     
30026     var s = document.createElement("span");
30027     s.className = "ytb-sep";
30028     if (cfg) {
30029         cfg.el = s;
30030     }
30031     
30032     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30033 };
30034 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30035     enable:Roo.emptyFn,
30036     disable:Roo.emptyFn,
30037     focus:Roo.emptyFn
30038 });
30039
30040 /**
30041  * @class Roo.Toolbar.Spacer
30042  * @extends Roo.Toolbar.Item
30043  * A simple element that adds extra horizontal space to a toolbar.
30044  * @constructor
30045  * Creates a new Spacer
30046  */
30047 Roo.Toolbar.Spacer = function(cfg){
30048     var s = document.createElement("div");
30049     s.className = "ytb-spacer";
30050     if (cfg) {
30051         cfg.el = s;
30052     }
30053     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30054 };
30055 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30056     enable:Roo.emptyFn,
30057     disable:Roo.emptyFn,
30058     focus:Roo.emptyFn
30059 });
30060
30061 /**
30062  * @class Roo.Toolbar.Fill
30063  * @extends Roo.Toolbar.Spacer
30064  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30065  * @constructor
30066  * Creates a new Spacer
30067  */
30068 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30069     // private
30070     render : function(td){
30071         td.style.width = '100%';
30072         Roo.Toolbar.Fill.superclass.render.call(this, td);
30073     }
30074 });
30075
30076 /**
30077  * @class Roo.Toolbar.TextItem
30078  * @extends Roo.Toolbar.Item
30079  * A simple class that renders text directly into a toolbar.
30080  * @constructor
30081  * Creates a new TextItem
30082  * @param {String} text
30083  */
30084 Roo.Toolbar.TextItem = function(cfg){
30085     var  text = cfg || "";
30086     if (typeof(cfg) == 'object') {
30087         text = cfg.text || "";
30088     }  else {
30089         cfg = null;
30090     }
30091     var s = document.createElement("span");
30092     s.className = "ytb-text";
30093     s.innerHTML = text;
30094     if (cfg) {
30095         cfg.el  = s;
30096     }
30097     
30098     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30099 };
30100 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30101     
30102      
30103     enable:Roo.emptyFn,
30104     disable:Roo.emptyFn,
30105     focus:Roo.emptyFn
30106 });
30107
30108 /**
30109  * @class Roo.Toolbar.Button
30110  * @extends Roo.Button
30111  * A button that renders into a toolbar.
30112  * @constructor
30113  * Creates a new Button
30114  * @param {Object} config A standard {@link Roo.Button} config object
30115  */
30116 Roo.Toolbar.Button = function(config){
30117     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30118 };
30119 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30120     render : function(td){
30121         this.td = td;
30122         Roo.Toolbar.Button.superclass.render.call(this, td);
30123     },
30124     
30125     /**
30126      * Removes and destroys this button
30127      */
30128     destroy : function(){
30129         Roo.Toolbar.Button.superclass.destroy.call(this);
30130         this.td.parentNode.removeChild(this.td);
30131     },
30132     
30133     /**
30134      * Shows this button
30135      */
30136     show: function(){
30137         this.hidden = false;
30138         this.td.style.display = "";
30139     },
30140     
30141     /**
30142      * Hides this button
30143      */
30144     hide: function(){
30145         this.hidden = true;
30146         this.td.style.display = "none";
30147     },
30148
30149     /**
30150      * Disables this item
30151      */
30152     disable : function(){
30153         Roo.fly(this.td).addClass("x-item-disabled");
30154         this.disabled = true;
30155     },
30156
30157     /**
30158      * Enables this item
30159      */
30160     enable : function(){
30161         Roo.fly(this.td).removeClass("x-item-disabled");
30162         this.disabled = false;
30163     }
30164 });
30165 // backwards compat
30166 Roo.ToolbarButton = Roo.Toolbar.Button;
30167
30168 /**
30169  * @class Roo.Toolbar.SplitButton
30170  * @extends Roo.SplitButton
30171  * A menu button that renders into a toolbar.
30172  * @constructor
30173  * Creates a new SplitButton
30174  * @param {Object} config A standard {@link Roo.SplitButton} config object
30175  */
30176 Roo.Toolbar.SplitButton = function(config){
30177     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30178 };
30179 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30180     render : function(td){
30181         this.td = td;
30182         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30183     },
30184     
30185     /**
30186      * Removes and destroys this button
30187      */
30188     destroy : function(){
30189         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30190         this.td.parentNode.removeChild(this.td);
30191     },
30192     
30193     /**
30194      * Shows this button
30195      */
30196     show: function(){
30197         this.hidden = false;
30198         this.td.style.display = "";
30199     },
30200     
30201     /**
30202      * Hides this button
30203      */
30204     hide: function(){
30205         this.hidden = true;
30206         this.td.style.display = "none";
30207     }
30208 });
30209
30210 // backwards compat
30211 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30212  * Based on:
30213  * Ext JS Library 1.1.1
30214  * Copyright(c) 2006-2007, Ext JS, LLC.
30215  *
30216  * Originally Released Under LGPL - original licence link has changed is not relivant.
30217  *
30218  * Fork - LGPL
30219  * <script type="text/javascript">
30220  */
30221  
30222 /**
30223  * @class Roo.PagingToolbar
30224  * @extends Roo.Toolbar
30225  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30226  * @constructor
30227  * Create a new PagingToolbar
30228  * @param {Object} config The config object
30229  */
30230 Roo.PagingToolbar = function(el, ds, config)
30231 {
30232     // old args format still supported... - xtype is prefered..
30233     if (typeof(el) == 'object' && el.xtype) {
30234         // created from xtype...
30235         config = el;
30236         ds = el.dataSource;
30237         el = config.container;
30238     }
30239     var items = [];
30240     if (config.items) {
30241         items = config.items;
30242         config.items = [];
30243     }
30244     
30245     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30246     this.ds = ds;
30247     this.cursor = 0;
30248     this.renderButtons(this.el);
30249     this.bind(ds);
30250     
30251     // supprot items array.
30252    
30253     Roo.each(items, function(e) {
30254         this.add(Roo.factory(e));
30255     },this);
30256     
30257 };
30258
30259 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30260     /**
30261      * @cfg {Roo.data.Store} dataSource
30262      * The underlying data store providing the paged data
30263      */
30264     /**
30265      * @cfg {String/HTMLElement/Element} container
30266      * container The id or element that will contain the toolbar
30267      */
30268     /**
30269      * @cfg {Boolean} displayInfo
30270      * True to display the displayMsg (defaults to false)
30271      */
30272     /**
30273      * @cfg {Number} pageSize
30274      * The number of records to display per page (defaults to 20)
30275      */
30276     pageSize: 20,
30277     /**
30278      * @cfg {String} displayMsg
30279      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30280      */
30281     displayMsg : 'Displaying {0} - {1} of {2}',
30282     /**
30283      * @cfg {String} emptyMsg
30284      * The message to display when no records are found (defaults to "No data to display")
30285      */
30286     emptyMsg : 'No data to display',
30287     /**
30288      * Customizable piece of the default paging text (defaults to "Page")
30289      * @type String
30290      */
30291     beforePageText : "Page",
30292     /**
30293      * Customizable piece of the default paging text (defaults to "of %0")
30294      * @type String
30295      */
30296     afterPageText : "of {0}",
30297     /**
30298      * Customizable piece of the default paging text (defaults to "First Page")
30299      * @type String
30300      */
30301     firstText : "First Page",
30302     /**
30303      * Customizable piece of the default paging text (defaults to "Previous Page")
30304      * @type String
30305      */
30306     prevText : "Previous Page",
30307     /**
30308      * Customizable piece of the default paging text (defaults to "Next Page")
30309      * @type String
30310      */
30311     nextText : "Next Page",
30312     /**
30313      * Customizable piece of the default paging text (defaults to "Last Page")
30314      * @type String
30315      */
30316     lastText : "Last Page",
30317     /**
30318      * Customizable piece of the default paging text (defaults to "Refresh")
30319      * @type String
30320      */
30321     refreshText : "Refresh",
30322
30323     // private
30324     renderButtons : function(el){
30325         Roo.PagingToolbar.superclass.render.call(this, el);
30326         this.first = this.addButton({
30327             tooltip: this.firstText,
30328             cls: "x-btn-icon x-grid-page-first",
30329             disabled: true,
30330             handler: this.onClick.createDelegate(this, ["first"])
30331         });
30332         this.prev = this.addButton({
30333             tooltip: this.prevText,
30334             cls: "x-btn-icon x-grid-page-prev",
30335             disabled: true,
30336             handler: this.onClick.createDelegate(this, ["prev"])
30337         });
30338         //this.addSeparator();
30339         this.add(this.beforePageText);
30340         this.field = Roo.get(this.addDom({
30341            tag: "input",
30342            type: "text",
30343            size: "3",
30344            value: "1",
30345            cls: "x-grid-page-number"
30346         }).el);
30347         this.field.on("keydown", this.onPagingKeydown, this);
30348         this.field.on("focus", function(){this.dom.select();});
30349         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30350         this.field.setHeight(18);
30351         //this.addSeparator();
30352         this.next = this.addButton({
30353             tooltip: this.nextText,
30354             cls: "x-btn-icon x-grid-page-next",
30355             disabled: true,
30356             handler: this.onClick.createDelegate(this, ["next"])
30357         });
30358         this.last = this.addButton({
30359             tooltip: this.lastText,
30360             cls: "x-btn-icon x-grid-page-last",
30361             disabled: true,
30362             handler: this.onClick.createDelegate(this, ["last"])
30363         });
30364         //this.addSeparator();
30365         this.loading = this.addButton({
30366             tooltip: this.refreshText,
30367             cls: "x-btn-icon x-grid-loading",
30368             handler: this.onClick.createDelegate(this, ["refresh"])
30369         });
30370
30371         if(this.displayInfo){
30372             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30373         }
30374     },
30375
30376     // private
30377     updateInfo : function(){
30378         if(this.displayEl){
30379             var count = this.ds.getCount();
30380             var msg = count == 0 ?
30381                 this.emptyMsg :
30382                 String.format(
30383                     this.displayMsg,
30384                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30385                 );
30386             this.displayEl.update(msg);
30387         }
30388     },
30389
30390     // private
30391     onLoad : function(ds, r, o){
30392        this.cursor = o.params ? o.params.start : 0;
30393        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30394
30395        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30396        this.field.dom.value = ap;
30397        this.first.setDisabled(ap == 1);
30398        this.prev.setDisabled(ap == 1);
30399        this.next.setDisabled(ap == ps);
30400        this.last.setDisabled(ap == ps);
30401        this.loading.enable();
30402        this.updateInfo();
30403     },
30404
30405     // private
30406     getPageData : function(){
30407         var total = this.ds.getTotalCount();
30408         return {
30409             total : total,
30410             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30411             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30412         };
30413     },
30414
30415     // private
30416     onLoadError : function(){
30417         this.loading.enable();
30418     },
30419
30420     // private
30421     onPagingKeydown : function(e){
30422         var k = e.getKey();
30423         var d = this.getPageData();
30424         if(k == e.RETURN){
30425             var v = this.field.dom.value, pageNum;
30426             if(!v || isNaN(pageNum = parseInt(v, 10))){
30427                 this.field.dom.value = d.activePage;
30428                 return;
30429             }
30430             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30431             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30432             e.stopEvent();
30433         }
30434         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))
30435         {
30436           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30437           this.field.dom.value = pageNum;
30438           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30439           e.stopEvent();
30440         }
30441         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30442         {
30443           var v = this.field.dom.value, pageNum; 
30444           var increment = (e.shiftKey) ? 10 : 1;
30445           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30446             increment *= -1;
30447           }
30448           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30449             this.field.dom.value = d.activePage;
30450             return;
30451           }
30452           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30453           {
30454             this.field.dom.value = parseInt(v, 10) + increment;
30455             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30456             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30457           }
30458           e.stopEvent();
30459         }
30460     },
30461
30462     // private
30463     beforeLoad : function(){
30464         if(this.loading){
30465             this.loading.disable();
30466         }
30467     },
30468
30469     // private
30470     onClick : function(which){
30471         var ds = this.ds;
30472         switch(which){
30473             case "first":
30474                 ds.load({params:{start: 0, limit: this.pageSize}});
30475             break;
30476             case "prev":
30477                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30478             break;
30479             case "next":
30480                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30481             break;
30482             case "last":
30483                 var total = ds.getTotalCount();
30484                 var extra = total % this.pageSize;
30485                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30486                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30487             break;
30488             case "refresh":
30489                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30490             break;
30491         }
30492     },
30493
30494     /**
30495      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30496      * @param {Roo.data.Store} store The data store to unbind
30497      */
30498     unbind : function(ds){
30499         ds.un("beforeload", this.beforeLoad, this);
30500         ds.un("load", this.onLoad, this);
30501         ds.un("loadexception", this.onLoadError, this);
30502         ds.un("remove", this.updateInfo, this);
30503         ds.un("add", this.updateInfo, this);
30504         this.ds = undefined;
30505     },
30506
30507     /**
30508      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30509      * @param {Roo.data.Store} store The data store to bind
30510      */
30511     bind : function(ds){
30512         ds.on("beforeload", this.beforeLoad, this);
30513         ds.on("load", this.onLoad, this);
30514         ds.on("loadexception", this.onLoadError, this);
30515         ds.on("remove", this.updateInfo, this);
30516         ds.on("add", this.updateInfo, this);
30517         this.ds = ds;
30518     }
30519 });/*
30520  * Based on:
30521  * Ext JS Library 1.1.1
30522  * Copyright(c) 2006-2007, Ext JS, LLC.
30523  *
30524  * Originally Released Under LGPL - original licence link has changed is not relivant.
30525  *
30526  * Fork - LGPL
30527  * <script type="text/javascript">
30528  */
30529
30530 /**
30531  * @class Roo.Resizable
30532  * @extends Roo.util.Observable
30533  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30534  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30535  * 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
30536  * the element will be wrapped for you automatically.</p>
30537  * <p>Here is the list of valid resize handles:</p>
30538  * <pre>
30539 Value   Description
30540 ------  -------------------
30541  'n'     north
30542  's'     south
30543  'e'     east
30544  'w'     west
30545  'nw'    northwest
30546  'sw'    southwest
30547  'se'    southeast
30548  'ne'    northeast
30549  'hd'    horizontal drag
30550  'all'   all
30551 </pre>
30552  * <p>Here's an example showing the creation of a typical Resizable:</p>
30553  * <pre><code>
30554 var resizer = new Roo.Resizable("element-id", {
30555     handles: 'all',
30556     minWidth: 200,
30557     minHeight: 100,
30558     maxWidth: 500,
30559     maxHeight: 400,
30560     pinned: true
30561 });
30562 resizer.on("resize", myHandler);
30563 </code></pre>
30564  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30565  * resizer.east.setDisplayed(false);</p>
30566  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30567  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30568  * resize operation's new size (defaults to [0, 0])
30569  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30570  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30571  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30572  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30573  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30574  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30575  * @cfg {Number} width The width of the element in pixels (defaults to null)
30576  * @cfg {Number} height The height of the element in pixels (defaults to null)
30577  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30578  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30579  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30580  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30581  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30582  * in favor of the handles config option (defaults to false)
30583  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30584  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30585  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30586  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30587  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30588  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30589  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30590  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30591  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30592  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30593  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30594  * @constructor
30595  * Create a new resizable component
30596  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30597  * @param {Object} config configuration options
30598   */
30599 Roo.Resizable = function(el, config)
30600 {
30601     this.el = Roo.get(el);
30602
30603     if(config && config.wrap){
30604         config.resizeChild = this.el;
30605         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30606         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30607         this.el.setStyle("overflow", "hidden");
30608         this.el.setPositioning(config.resizeChild.getPositioning());
30609         config.resizeChild.clearPositioning();
30610         if(!config.width || !config.height){
30611             var csize = config.resizeChild.getSize();
30612             this.el.setSize(csize.width, csize.height);
30613         }
30614         if(config.pinned && !config.adjustments){
30615             config.adjustments = "auto";
30616         }
30617     }
30618
30619     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30620     this.proxy.unselectable();
30621     this.proxy.enableDisplayMode('block');
30622
30623     Roo.apply(this, config);
30624
30625     if(this.pinned){
30626         this.disableTrackOver = true;
30627         this.el.addClass("x-resizable-pinned");
30628     }
30629     // if the element isn't positioned, make it relative
30630     var position = this.el.getStyle("position");
30631     if(position != "absolute" && position != "fixed"){
30632         this.el.setStyle("position", "relative");
30633     }
30634     if(!this.handles){ // no handles passed, must be legacy style
30635         this.handles = 's,e,se';
30636         if(this.multiDirectional){
30637             this.handles += ',n,w';
30638         }
30639     }
30640     if(this.handles == "all"){
30641         this.handles = "n s e w ne nw se sw";
30642     }
30643     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30644     var ps = Roo.Resizable.positions;
30645     for(var i = 0, len = hs.length; i < len; i++){
30646         if(hs[i] && ps[hs[i]]){
30647             var pos = ps[hs[i]];
30648             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30649         }
30650     }
30651     // legacy
30652     this.corner = this.southeast;
30653     
30654     // updateBox = the box can move..
30655     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30656         this.updateBox = true;
30657     }
30658
30659     this.activeHandle = null;
30660
30661     if(this.resizeChild){
30662         if(typeof this.resizeChild == "boolean"){
30663             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30664         }else{
30665             this.resizeChild = Roo.get(this.resizeChild, true);
30666         }
30667     }
30668     
30669     if(this.adjustments == "auto"){
30670         var rc = this.resizeChild;
30671         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30672         if(rc && (hw || hn)){
30673             rc.position("relative");
30674             rc.setLeft(hw ? hw.el.getWidth() : 0);
30675             rc.setTop(hn ? hn.el.getHeight() : 0);
30676         }
30677         this.adjustments = [
30678             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30679             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30680         ];
30681     }
30682
30683     if(this.draggable){
30684         this.dd = this.dynamic ?
30685             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30686         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30687     }
30688
30689     // public events
30690     this.addEvents({
30691         /**
30692          * @event beforeresize
30693          * Fired before resize is allowed. Set enabled to false to cancel resize.
30694          * @param {Roo.Resizable} this
30695          * @param {Roo.EventObject} e The mousedown event
30696          */
30697         "beforeresize" : true,
30698         /**
30699          * @event resizing
30700          * Fired a resizing.
30701          * @param {Roo.Resizable} this
30702          * @param {Number} x The new x position
30703          * @param {Number} y The new y position
30704          * @param {Number} w The new w width
30705          * @param {Number} h The new h hight
30706          * @param {Roo.EventObject} e The mouseup event
30707          */
30708         "resizing" : true,
30709         /**
30710          * @event resize
30711          * Fired after a resize.
30712          * @param {Roo.Resizable} this
30713          * @param {Number} width The new width
30714          * @param {Number} height The new height
30715          * @param {Roo.EventObject} e The mouseup event
30716          */
30717         "resize" : true
30718     });
30719
30720     if(this.width !== null && this.height !== null){
30721         this.resizeTo(this.width, this.height);
30722     }else{
30723         this.updateChildSize();
30724     }
30725     if(Roo.isIE){
30726         this.el.dom.style.zoom = 1;
30727     }
30728     Roo.Resizable.superclass.constructor.call(this);
30729 };
30730
30731 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30732         resizeChild : false,
30733         adjustments : [0, 0],
30734         minWidth : 5,
30735         minHeight : 5,
30736         maxWidth : 10000,
30737         maxHeight : 10000,
30738         enabled : true,
30739         animate : false,
30740         duration : .35,
30741         dynamic : false,
30742         handles : false,
30743         multiDirectional : false,
30744         disableTrackOver : false,
30745         easing : 'easeOutStrong',
30746         widthIncrement : 0,
30747         heightIncrement : 0,
30748         pinned : false,
30749         width : null,
30750         height : null,
30751         preserveRatio : false,
30752         transparent: false,
30753         minX: 0,
30754         minY: 0,
30755         draggable: false,
30756
30757         /**
30758          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30759          */
30760         constrainTo: undefined,
30761         /**
30762          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30763          */
30764         resizeRegion: undefined,
30765
30766
30767     /**
30768      * Perform a manual resize
30769      * @param {Number} width
30770      * @param {Number} height
30771      */
30772     resizeTo : function(width, height){
30773         this.el.setSize(width, height);
30774         this.updateChildSize();
30775         this.fireEvent("resize", this, width, height, null);
30776     },
30777
30778     // private
30779     startSizing : function(e, handle){
30780         this.fireEvent("beforeresize", this, e);
30781         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30782
30783             if(!this.overlay){
30784                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30785                 this.overlay.unselectable();
30786                 this.overlay.enableDisplayMode("block");
30787                 this.overlay.on("mousemove", this.onMouseMove, this);
30788                 this.overlay.on("mouseup", this.onMouseUp, this);
30789             }
30790             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30791
30792             this.resizing = true;
30793             this.startBox = this.el.getBox();
30794             this.startPoint = e.getXY();
30795             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30796                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30797
30798             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30799             this.overlay.show();
30800
30801             if(this.constrainTo) {
30802                 var ct = Roo.get(this.constrainTo);
30803                 this.resizeRegion = ct.getRegion().adjust(
30804                     ct.getFrameWidth('t'),
30805                     ct.getFrameWidth('l'),
30806                     -ct.getFrameWidth('b'),
30807                     -ct.getFrameWidth('r')
30808                 );
30809             }
30810
30811             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30812             this.proxy.show();
30813             this.proxy.setBox(this.startBox);
30814             if(!this.dynamic){
30815                 this.proxy.setStyle('visibility', 'visible');
30816             }
30817         }
30818     },
30819
30820     // private
30821     onMouseDown : function(handle, e){
30822         if(this.enabled){
30823             e.stopEvent();
30824             this.activeHandle = handle;
30825             this.startSizing(e, handle);
30826         }
30827     },
30828
30829     // private
30830     onMouseUp : function(e){
30831         var size = this.resizeElement();
30832         this.resizing = false;
30833         this.handleOut();
30834         this.overlay.hide();
30835         this.proxy.hide();
30836         this.fireEvent("resize", this, size.width, size.height, e);
30837     },
30838
30839     // private
30840     updateChildSize : function(){
30841         
30842         if(this.resizeChild){
30843             var el = this.el;
30844             var child = this.resizeChild;
30845             var adj = this.adjustments;
30846             if(el.dom.offsetWidth){
30847                 var b = el.getSize(true);
30848                 child.setSize(b.width+adj[0], b.height+adj[1]);
30849             }
30850             // Second call here for IE
30851             // The first call enables instant resizing and
30852             // the second call corrects scroll bars if they
30853             // exist
30854             if(Roo.isIE){
30855                 setTimeout(function(){
30856                     if(el.dom.offsetWidth){
30857                         var b = el.getSize(true);
30858                         child.setSize(b.width+adj[0], b.height+adj[1]);
30859                     }
30860                 }, 10);
30861             }
30862         }
30863     },
30864
30865     // private
30866     snap : function(value, inc, min){
30867         if(!inc || !value) {
30868             return value;
30869         }
30870         var newValue = value;
30871         var m = value % inc;
30872         if(m > 0){
30873             if(m > (inc/2)){
30874                 newValue = value + (inc-m);
30875             }else{
30876                 newValue = value - m;
30877             }
30878         }
30879         return Math.max(min, newValue);
30880     },
30881
30882     // private
30883     resizeElement : function(){
30884         var box = this.proxy.getBox();
30885         if(this.updateBox){
30886             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
30887         }else{
30888             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
30889         }
30890         this.updateChildSize();
30891         if(!this.dynamic){
30892             this.proxy.hide();
30893         }
30894         return box;
30895     },
30896
30897     // private
30898     constrain : function(v, diff, m, mx){
30899         if(v - diff < m){
30900             diff = v - m;
30901         }else if(v - diff > mx){
30902             diff = mx - v;
30903         }
30904         return diff;
30905     },
30906
30907     // private
30908     onMouseMove : function(e){
30909         
30910         if(this.enabled){
30911             try{// try catch so if something goes wrong the user doesn't get hung
30912
30913             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
30914                 return;
30915             }
30916
30917             //var curXY = this.startPoint;
30918             var curSize = this.curSize || this.startBox;
30919             var x = this.startBox.x, y = this.startBox.y;
30920             var ox = x, oy = y;
30921             var w = curSize.width, h = curSize.height;
30922             var ow = w, oh = h;
30923             var mw = this.minWidth, mh = this.minHeight;
30924             var mxw = this.maxWidth, mxh = this.maxHeight;
30925             var wi = this.widthIncrement;
30926             var hi = this.heightIncrement;
30927
30928             var eventXY = e.getXY();
30929             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
30930             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
30931
30932             var pos = this.activeHandle.position;
30933
30934             switch(pos){
30935                 case "east":
30936                     w += diffX;
30937                     w = Math.min(Math.max(mw, w), mxw);
30938                     break;
30939              
30940                 case "south":
30941                     h += diffY;
30942                     h = Math.min(Math.max(mh, h), mxh);
30943                     break;
30944                 case "southeast":
30945                     w += diffX;
30946                     h += diffY;
30947                     w = Math.min(Math.max(mw, w), mxw);
30948                     h = Math.min(Math.max(mh, h), mxh);
30949                     break;
30950                 case "north":
30951                     diffY = this.constrain(h, diffY, mh, mxh);
30952                     y += diffY;
30953                     h -= diffY;
30954                     break;
30955                 case "hdrag":
30956                     
30957                     if (wi) {
30958                         var adiffX = Math.abs(diffX);
30959                         var sub = (adiffX % wi); // how much 
30960                         if (sub > (wi/2)) { // far enough to snap
30961                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
30962                         } else {
30963                             // remove difference.. 
30964                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
30965                         }
30966                     }
30967                     x += diffX;
30968                     x = Math.max(this.minX, x);
30969                     break;
30970                 case "west":
30971                     diffX = this.constrain(w, diffX, mw, mxw);
30972                     x += diffX;
30973                     w -= diffX;
30974                     break;
30975                 case "northeast":
30976                     w += diffX;
30977                     w = Math.min(Math.max(mw, w), mxw);
30978                     diffY = this.constrain(h, diffY, mh, mxh);
30979                     y += diffY;
30980                     h -= diffY;
30981                     break;
30982                 case "northwest":
30983                     diffX = this.constrain(w, diffX, mw, mxw);
30984                     diffY = this.constrain(h, diffY, mh, mxh);
30985                     y += diffY;
30986                     h -= diffY;
30987                     x += diffX;
30988                     w -= diffX;
30989                     break;
30990                case "southwest":
30991                     diffX = this.constrain(w, diffX, mw, mxw);
30992                     h += diffY;
30993                     h = Math.min(Math.max(mh, h), mxh);
30994                     x += diffX;
30995                     w -= diffX;
30996                     break;
30997             }
30998
30999             var sw = this.snap(w, wi, mw);
31000             var sh = this.snap(h, hi, mh);
31001             if(sw != w || sh != h){
31002                 switch(pos){
31003                     case "northeast":
31004                         y -= sh - h;
31005                     break;
31006                     case "north":
31007                         y -= sh - h;
31008                         break;
31009                     case "southwest":
31010                         x -= sw - w;
31011                     break;
31012                     case "west":
31013                         x -= sw - w;
31014                         break;
31015                     case "northwest":
31016                         x -= sw - w;
31017                         y -= sh - h;
31018                     break;
31019                 }
31020                 w = sw;
31021                 h = sh;
31022             }
31023
31024             if(this.preserveRatio){
31025                 switch(pos){
31026                     case "southeast":
31027                     case "east":
31028                         h = oh * (w/ow);
31029                         h = Math.min(Math.max(mh, h), mxh);
31030                         w = ow * (h/oh);
31031                        break;
31032                     case "south":
31033                         w = ow * (h/oh);
31034                         w = Math.min(Math.max(mw, w), mxw);
31035                         h = oh * (w/ow);
31036                         break;
31037                     case "northeast":
31038                         w = ow * (h/oh);
31039                         w = Math.min(Math.max(mw, w), mxw);
31040                         h = oh * (w/ow);
31041                     break;
31042                     case "north":
31043                         var tw = w;
31044                         w = ow * (h/oh);
31045                         w = Math.min(Math.max(mw, w), mxw);
31046                         h = oh * (w/ow);
31047                         x += (tw - w) / 2;
31048                         break;
31049                     case "southwest":
31050                         h = oh * (w/ow);
31051                         h = Math.min(Math.max(mh, h), mxh);
31052                         var tw = w;
31053                         w = ow * (h/oh);
31054                         x += tw - w;
31055                         break;
31056                     case "west":
31057                         var th = h;
31058                         h = oh * (w/ow);
31059                         h = Math.min(Math.max(mh, h), mxh);
31060                         y += (th - h) / 2;
31061                         var tw = w;
31062                         w = ow * (h/oh);
31063                         x += tw - w;
31064                        break;
31065                     case "northwest":
31066                         var tw = w;
31067                         var th = h;
31068                         h = oh * (w/ow);
31069                         h = Math.min(Math.max(mh, h), mxh);
31070                         w = ow * (h/oh);
31071                         y += th - h;
31072                         x += tw - w;
31073                        break;
31074
31075                 }
31076             }
31077             if (pos == 'hdrag') {
31078                 w = ow;
31079             }
31080             this.proxy.setBounds(x, y, w, h);
31081             if(this.dynamic){
31082                 this.resizeElement();
31083             }
31084             }catch(e){}
31085         }
31086         this.fireEvent("resizing", this, x, y, w, h, e);
31087     },
31088
31089     // private
31090     handleOver : function(){
31091         if(this.enabled){
31092             this.el.addClass("x-resizable-over");
31093         }
31094     },
31095
31096     // private
31097     handleOut : function(){
31098         if(!this.resizing){
31099             this.el.removeClass("x-resizable-over");
31100         }
31101     },
31102
31103     /**
31104      * Returns the element this component is bound to.
31105      * @return {Roo.Element}
31106      */
31107     getEl : function(){
31108         return this.el;
31109     },
31110
31111     /**
31112      * Returns the resizeChild element (or null).
31113      * @return {Roo.Element}
31114      */
31115     getResizeChild : function(){
31116         return this.resizeChild;
31117     },
31118     groupHandler : function()
31119     {
31120         
31121     },
31122     /**
31123      * Destroys this resizable. If the element was wrapped and
31124      * removeEl is not true then the element remains.
31125      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31126      */
31127     destroy : function(removeEl){
31128         this.proxy.remove();
31129         if(this.overlay){
31130             this.overlay.removeAllListeners();
31131             this.overlay.remove();
31132         }
31133         var ps = Roo.Resizable.positions;
31134         for(var k in ps){
31135             if(typeof ps[k] != "function" && this[ps[k]]){
31136                 var h = this[ps[k]];
31137                 h.el.removeAllListeners();
31138                 h.el.remove();
31139             }
31140         }
31141         if(removeEl){
31142             this.el.update("");
31143             this.el.remove();
31144         }
31145     }
31146 });
31147
31148 // private
31149 // hash to map config positions to true positions
31150 Roo.Resizable.positions = {
31151     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31152     hd: "hdrag"
31153 };
31154
31155 // private
31156 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31157     if(!this.tpl){
31158         // only initialize the template if resizable is used
31159         var tpl = Roo.DomHelper.createTemplate(
31160             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31161         );
31162         tpl.compile();
31163         Roo.Resizable.Handle.prototype.tpl = tpl;
31164     }
31165     this.position = pos;
31166     this.rz = rz;
31167     // show north drag fro topdra
31168     var handlepos = pos == 'hdrag' ? 'north' : pos;
31169     
31170     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31171     if (pos == 'hdrag') {
31172         this.el.setStyle('cursor', 'pointer');
31173     }
31174     this.el.unselectable();
31175     if(transparent){
31176         this.el.setOpacity(0);
31177     }
31178     this.el.on("mousedown", this.onMouseDown, this);
31179     if(!disableTrackOver){
31180         this.el.on("mouseover", this.onMouseOver, this);
31181         this.el.on("mouseout", this.onMouseOut, this);
31182     }
31183 };
31184
31185 // private
31186 Roo.Resizable.Handle.prototype = {
31187     afterResize : function(rz){
31188         Roo.log('after?');
31189         // do nothing
31190     },
31191     // private
31192     onMouseDown : function(e){
31193         this.rz.onMouseDown(this, e);
31194     },
31195     // private
31196     onMouseOver : function(e){
31197         this.rz.handleOver(this, e);
31198     },
31199     // private
31200     onMouseOut : function(e){
31201         this.rz.handleOut(this, e);
31202     }
31203 };/*
31204  * Based on:
31205  * Ext JS Library 1.1.1
31206  * Copyright(c) 2006-2007, Ext JS, LLC.
31207  *
31208  * Originally Released Under LGPL - original licence link has changed is not relivant.
31209  *
31210  * Fork - LGPL
31211  * <script type="text/javascript">
31212  */
31213
31214 /**
31215  * @class Roo.Editor
31216  * @extends Roo.Component
31217  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31218  * @constructor
31219  * Create a new Editor
31220  * @param {Roo.form.Field} field The Field object (or descendant)
31221  * @param {Object} config The config object
31222  */
31223 Roo.Editor = function(field, config){
31224     Roo.Editor.superclass.constructor.call(this, config);
31225     this.field = field;
31226     this.addEvents({
31227         /**
31228              * @event beforestartedit
31229              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31230              * false from the handler of this event.
31231              * @param {Editor} this
31232              * @param {Roo.Element} boundEl The underlying element bound to this editor
31233              * @param {Mixed} value The field value being set
31234              */
31235         "beforestartedit" : true,
31236         /**
31237              * @event startedit
31238              * Fires when this editor is displayed
31239              * @param {Roo.Element} boundEl The underlying element bound to this editor
31240              * @param {Mixed} value The starting field value
31241              */
31242         "startedit" : true,
31243         /**
31244              * @event beforecomplete
31245              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31246              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31247              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31248              * event will not fire since no edit actually occurred.
31249              * @param {Editor} this
31250              * @param {Mixed} value The current field value
31251              * @param {Mixed} startValue The original field value
31252              */
31253         "beforecomplete" : true,
31254         /**
31255              * @event complete
31256              * Fires after editing is complete and any changed value has been written to the underlying field.
31257              * @param {Editor} this
31258              * @param {Mixed} value The current field value
31259              * @param {Mixed} startValue The original field value
31260              */
31261         "complete" : true,
31262         /**
31263          * @event specialkey
31264          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31265          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31266          * @param {Roo.form.Field} this
31267          * @param {Roo.EventObject} e The event object
31268          */
31269         "specialkey" : true
31270     });
31271 };
31272
31273 Roo.extend(Roo.Editor, Roo.Component, {
31274     /**
31275      * @cfg {Boolean/String} autosize
31276      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31277      * or "height" to adopt the height only (defaults to false)
31278      */
31279     /**
31280      * @cfg {Boolean} revertInvalid
31281      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31282      * validation fails (defaults to true)
31283      */
31284     /**
31285      * @cfg {Boolean} ignoreNoChange
31286      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31287      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31288      * will never be ignored.
31289      */
31290     /**
31291      * @cfg {Boolean} hideEl
31292      * False to keep the bound element visible while the editor is displayed (defaults to true)
31293      */
31294     /**
31295      * @cfg {Mixed} value
31296      * The data value of the underlying field (defaults to "")
31297      */
31298     value : "",
31299     /**
31300      * @cfg {String} alignment
31301      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31302      */
31303     alignment: "c-c?",
31304     /**
31305      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31306      * for bottom-right shadow (defaults to "frame")
31307      */
31308     shadow : "frame",
31309     /**
31310      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31311      */
31312     constrain : false,
31313     /**
31314      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31315      */
31316     completeOnEnter : false,
31317     /**
31318      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31319      */
31320     cancelOnEsc : false,
31321     /**
31322      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31323      */
31324     updateEl : false,
31325
31326     // private
31327     onRender : function(ct, position){
31328         this.el = new Roo.Layer({
31329             shadow: this.shadow,
31330             cls: "x-editor",
31331             parentEl : ct,
31332             shim : this.shim,
31333             shadowOffset:4,
31334             id: this.id,
31335             constrain: this.constrain
31336         });
31337         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31338         if(this.field.msgTarget != 'title'){
31339             this.field.msgTarget = 'qtip';
31340         }
31341         this.field.render(this.el);
31342         if(Roo.isGecko){
31343             this.field.el.dom.setAttribute('autocomplete', 'off');
31344         }
31345         this.field.on("specialkey", this.onSpecialKey, this);
31346         if(this.swallowKeys){
31347             this.field.el.swallowEvent(['keydown','keypress']);
31348         }
31349         this.field.show();
31350         this.field.on("blur", this.onBlur, this);
31351         if(this.field.grow){
31352             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31353         }
31354     },
31355
31356     onSpecialKey : function(field, e)
31357     {
31358         //Roo.log('editor onSpecialKey');
31359         if(this.completeOnEnter && e.getKey() == e.ENTER){
31360             e.stopEvent();
31361             this.completeEdit();
31362             return;
31363         }
31364         // do not fire special key otherwise it might hide close the editor...
31365         if(e.getKey() == e.ENTER){    
31366             return;
31367         }
31368         if(this.cancelOnEsc && e.getKey() == e.ESC){
31369             this.cancelEdit();
31370             return;
31371         } 
31372         this.fireEvent('specialkey', field, e);
31373     
31374     },
31375
31376     /**
31377      * Starts the editing process and shows the editor.
31378      * @param {String/HTMLElement/Element} el The element to edit
31379      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31380       * to the innerHTML of el.
31381      */
31382     startEdit : function(el, value){
31383         if(this.editing){
31384             this.completeEdit();
31385         }
31386         this.boundEl = Roo.get(el);
31387         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31388         if(!this.rendered){
31389             this.render(this.parentEl || document.body);
31390         }
31391         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31392             return;
31393         }
31394         this.startValue = v;
31395         this.field.setValue(v);
31396         if(this.autoSize){
31397             var sz = this.boundEl.getSize();
31398             switch(this.autoSize){
31399                 case "width":
31400                 this.setSize(sz.width,  "");
31401                 break;
31402                 case "height":
31403                 this.setSize("",  sz.height);
31404                 break;
31405                 default:
31406                 this.setSize(sz.width,  sz.height);
31407             }
31408         }
31409         this.el.alignTo(this.boundEl, this.alignment);
31410         this.editing = true;
31411         if(Roo.QuickTips){
31412             Roo.QuickTips.disable();
31413         }
31414         this.show();
31415     },
31416
31417     /**
31418      * Sets the height and width of this editor.
31419      * @param {Number} width The new width
31420      * @param {Number} height The new height
31421      */
31422     setSize : function(w, h){
31423         this.field.setSize(w, h);
31424         if(this.el){
31425             this.el.sync();
31426         }
31427     },
31428
31429     /**
31430      * Realigns the editor to the bound field based on the current alignment config value.
31431      */
31432     realign : function(){
31433         this.el.alignTo(this.boundEl, this.alignment);
31434     },
31435
31436     /**
31437      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31438      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31439      */
31440     completeEdit : function(remainVisible){
31441         if(!this.editing){
31442             return;
31443         }
31444         var v = this.getValue();
31445         if(this.revertInvalid !== false && !this.field.isValid()){
31446             v = this.startValue;
31447             this.cancelEdit(true);
31448         }
31449         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31450             this.editing = false;
31451             this.hide();
31452             return;
31453         }
31454         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31455             this.editing = false;
31456             if(this.updateEl && this.boundEl){
31457                 this.boundEl.update(v);
31458             }
31459             if(remainVisible !== true){
31460                 this.hide();
31461             }
31462             this.fireEvent("complete", this, v, this.startValue);
31463         }
31464     },
31465
31466     // private
31467     onShow : function(){
31468         this.el.show();
31469         if(this.hideEl !== false){
31470             this.boundEl.hide();
31471         }
31472         this.field.show();
31473         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31474             this.fixIEFocus = true;
31475             this.deferredFocus.defer(50, this);
31476         }else{
31477             this.field.focus();
31478         }
31479         this.fireEvent("startedit", this.boundEl, this.startValue);
31480     },
31481
31482     deferredFocus : function(){
31483         if(this.editing){
31484             this.field.focus();
31485         }
31486     },
31487
31488     /**
31489      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31490      * reverted to the original starting value.
31491      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31492      * cancel (defaults to false)
31493      */
31494     cancelEdit : function(remainVisible){
31495         if(this.editing){
31496             this.setValue(this.startValue);
31497             if(remainVisible !== true){
31498                 this.hide();
31499             }
31500         }
31501     },
31502
31503     // private
31504     onBlur : function(){
31505         if(this.allowBlur !== true && this.editing){
31506             this.completeEdit();
31507         }
31508     },
31509
31510     // private
31511     onHide : function(){
31512         if(this.editing){
31513             this.completeEdit();
31514             return;
31515         }
31516         this.field.blur();
31517         if(this.field.collapse){
31518             this.field.collapse();
31519         }
31520         this.el.hide();
31521         if(this.hideEl !== false){
31522             this.boundEl.show();
31523         }
31524         if(Roo.QuickTips){
31525             Roo.QuickTips.enable();
31526         }
31527     },
31528
31529     /**
31530      * Sets the data value of the editor
31531      * @param {Mixed} value Any valid value supported by the underlying field
31532      */
31533     setValue : function(v){
31534         this.field.setValue(v);
31535     },
31536
31537     /**
31538      * Gets the data value of the editor
31539      * @return {Mixed} The data value
31540      */
31541     getValue : function(){
31542         return this.field.getValue();
31543     }
31544 });/*
31545  * Based on:
31546  * Ext JS Library 1.1.1
31547  * Copyright(c) 2006-2007, Ext JS, LLC.
31548  *
31549  * Originally Released Under LGPL - original licence link has changed is not relivant.
31550  *
31551  * Fork - LGPL
31552  * <script type="text/javascript">
31553  */
31554  
31555 /**
31556  * @class Roo.BasicDialog
31557  * @extends Roo.util.Observable
31558  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31559  * <pre><code>
31560 var dlg = new Roo.BasicDialog("my-dlg", {
31561     height: 200,
31562     width: 300,
31563     minHeight: 100,
31564     minWidth: 150,
31565     modal: true,
31566     proxyDrag: true,
31567     shadow: true
31568 });
31569 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31570 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31571 dlg.addButton('Cancel', dlg.hide, dlg);
31572 dlg.show();
31573 </code></pre>
31574   <b>A Dialog should always be a direct child of the body element.</b>
31575  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31576  * @cfg {String} title Default text to display in the title bar (defaults to null)
31577  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31578  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31579  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31580  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31581  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31582  * (defaults to null with no animation)
31583  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31584  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31585  * property for valid values (defaults to 'all')
31586  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31587  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31588  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31589  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31590  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31591  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31592  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31593  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31594  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31595  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31596  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31597  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31598  * draggable = true (defaults to false)
31599  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31600  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31601  * shadow (defaults to false)
31602  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31603  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31604  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31605  * @cfg {Array} buttons Array of buttons
31606  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31607  * @constructor
31608  * Create a new BasicDialog.
31609  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31610  * @param {Object} config Configuration options
31611  */
31612 Roo.BasicDialog = function(el, config){
31613     this.el = Roo.get(el);
31614     var dh = Roo.DomHelper;
31615     if(!this.el && config && config.autoCreate){
31616         if(typeof config.autoCreate == "object"){
31617             if(!config.autoCreate.id){
31618                 config.autoCreate.id = el;
31619             }
31620             this.el = dh.append(document.body,
31621                         config.autoCreate, true);
31622         }else{
31623             this.el = dh.append(document.body,
31624                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31625         }
31626     }
31627     el = this.el;
31628     el.setDisplayed(true);
31629     el.hide = this.hideAction;
31630     this.id = el.id;
31631     el.addClass("x-dlg");
31632
31633     Roo.apply(this, config);
31634
31635     this.proxy = el.createProxy("x-dlg-proxy");
31636     this.proxy.hide = this.hideAction;
31637     this.proxy.setOpacity(.5);
31638     this.proxy.hide();
31639
31640     if(config.width){
31641         el.setWidth(config.width);
31642     }
31643     if(config.height){
31644         el.setHeight(config.height);
31645     }
31646     this.size = el.getSize();
31647     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31648         this.xy = [config.x,config.y];
31649     }else{
31650         this.xy = el.getCenterXY(true);
31651     }
31652     /** The header element @type Roo.Element */
31653     this.header = el.child("> .x-dlg-hd");
31654     /** The body element @type Roo.Element */
31655     this.body = el.child("> .x-dlg-bd");
31656     /** The footer element @type Roo.Element */
31657     this.footer = el.child("> .x-dlg-ft");
31658
31659     if(!this.header){
31660         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31661     }
31662     if(!this.body){
31663         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31664     }
31665
31666     this.header.unselectable();
31667     if(this.title){
31668         this.header.update(this.title);
31669     }
31670     // this element allows the dialog to be focused for keyboard event
31671     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31672     this.focusEl.swallowEvent("click", true);
31673
31674     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31675
31676     // wrap the body and footer for special rendering
31677     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31678     if(this.footer){
31679         this.bwrap.dom.appendChild(this.footer.dom);
31680     }
31681
31682     this.bg = this.el.createChild({
31683         tag: "div", cls:"x-dlg-bg",
31684         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31685     });
31686     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31687
31688
31689     if(this.autoScroll !== false && !this.autoTabs){
31690         this.body.setStyle("overflow", "auto");
31691     }
31692
31693     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31694
31695     if(this.closable !== false){
31696         this.el.addClass("x-dlg-closable");
31697         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31698         this.close.on("click", this.closeClick, this);
31699         this.close.addClassOnOver("x-dlg-close-over");
31700     }
31701     if(this.collapsible !== false){
31702         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31703         this.collapseBtn.on("click", this.collapseClick, this);
31704         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31705         this.header.on("dblclick", this.collapseClick, this);
31706     }
31707     if(this.resizable !== false){
31708         this.el.addClass("x-dlg-resizable");
31709         this.resizer = new Roo.Resizable(el, {
31710             minWidth: this.minWidth || 80,
31711             minHeight:this.minHeight || 80,
31712             handles: this.resizeHandles || "all",
31713             pinned: true
31714         });
31715         this.resizer.on("beforeresize", this.beforeResize, this);
31716         this.resizer.on("resize", this.onResize, this);
31717     }
31718     if(this.draggable !== false){
31719         el.addClass("x-dlg-draggable");
31720         if (!this.proxyDrag) {
31721             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31722         }
31723         else {
31724             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31725         }
31726         dd.setHandleElId(this.header.id);
31727         dd.endDrag = this.endMove.createDelegate(this);
31728         dd.startDrag = this.startMove.createDelegate(this);
31729         dd.onDrag = this.onDrag.createDelegate(this);
31730         dd.scroll = false;
31731         this.dd = dd;
31732     }
31733     if(this.modal){
31734         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31735         this.mask.enableDisplayMode("block");
31736         this.mask.hide();
31737         this.el.addClass("x-dlg-modal");
31738     }
31739     if(this.shadow){
31740         this.shadow = new Roo.Shadow({
31741             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31742             offset : this.shadowOffset
31743         });
31744     }else{
31745         this.shadowOffset = 0;
31746     }
31747     if(Roo.useShims && this.shim !== false){
31748         this.shim = this.el.createShim();
31749         this.shim.hide = this.hideAction;
31750         this.shim.hide();
31751     }else{
31752         this.shim = false;
31753     }
31754     if(this.autoTabs){
31755         this.initTabs();
31756     }
31757     if (this.buttons) { 
31758         var bts= this.buttons;
31759         this.buttons = [];
31760         Roo.each(bts, function(b) {
31761             this.addButton(b);
31762         }, this);
31763     }
31764     
31765     
31766     this.addEvents({
31767         /**
31768          * @event keydown
31769          * Fires when a key is pressed
31770          * @param {Roo.BasicDialog} this
31771          * @param {Roo.EventObject} e
31772          */
31773         "keydown" : true,
31774         /**
31775          * @event move
31776          * Fires when this dialog is moved by the user.
31777          * @param {Roo.BasicDialog} this
31778          * @param {Number} x The new page X
31779          * @param {Number} y The new page Y
31780          */
31781         "move" : true,
31782         /**
31783          * @event resize
31784          * Fires when this dialog is resized by the user.
31785          * @param {Roo.BasicDialog} this
31786          * @param {Number} width The new width
31787          * @param {Number} height The new height
31788          */
31789         "resize" : true,
31790         /**
31791          * @event beforehide
31792          * Fires before this dialog is hidden.
31793          * @param {Roo.BasicDialog} this
31794          */
31795         "beforehide" : true,
31796         /**
31797          * @event hide
31798          * Fires when this dialog is hidden.
31799          * @param {Roo.BasicDialog} this
31800          */
31801         "hide" : true,
31802         /**
31803          * @event beforeshow
31804          * Fires before this dialog is shown.
31805          * @param {Roo.BasicDialog} this
31806          */
31807         "beforeshow" : true,
31808         /**
31809          * @event show
31810          * Fires when this dialog is shown.
31811          * @param {Roo.BasicDialog} this
31812          */
31813         "show" : true
31814     });
31815     el.on("keydown", this.onKeyDown, this);
31816     el.on("mousedown", this.toFront, this);
31817     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31818     this.el.hide();
31819     Roo.DialogManager.register(this);
31820     Roo.BasicDialog.superclass.constructor.call(this);
31821 };
31822
31823 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31824     shadowOffset: Roo.isIE ? 6 : 5,
31825     minHeight: 80,
31826     minWidth: 200,
31827     minButtonWidth: 75,
31828     defaultButton: null,
31829     buttonAlign: "right",
31830     tabTag: 'div',
31831     firstShow: true,
31832
31833     /**
31834      * Sets the dialog title text
31835      * @param {String} text The title text to display
31836      * @return {Roo.BasicDialog} this
31837      */
31838     setTitle : function(text){
31839         this.header.update(text);
31840         return this;
31841     },
31842
31843     // private
31844     closeClick : function(){
31845         this.hide();
31846     },
31847
31848     // private
31849     collapseClick : function(){
31850         this[this.collapsed ? "expand" : "collapse"]();
31851     },
31852
31853     /**
31854      * Collapses the dialog to its minimized state (only the title bar is visible).
31855      * Equivalent to the user clicking the collapse dialog button.
31856      */
31857     collapse : function(){
31858         if(!this.collapsed){
31859             this.collapsed = true;
31860             this.el.addClass("x-dlg-collapsed");
31861             this.restoreHeight = this.el.getHeight();
31862             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31863         }
31864     },
31865
31866     /**
31867      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
31868      * clicking the expand dialog button.
31869      */
31870     expand : function(){
31871         if(this.collapsed){
31872             this.collapsed = false;
31873             this.el.removeClass("x-dlg-collapsed");
31874             this.resizeTo(this.el.getWidth(), this.restoreHeight);
31875         }
31876     },
31877
31878     /**
31879      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
31880      * @return {Roo.TabPanel} The tabs component
31881      */
31882     initTabs : function(){
31883         var tabs = this.getTabs();
31884         while(tabs.getTab(0)){
31885             tabs.removeTab(0);
31886         }
31887         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
31888             var dom = el.dom;
31889             tabs.addTab(Roo.id(dom), dom.title);
31890             dom.title = "";
31891         });
31892         tabs.activate(0);
31893         return tabs;
31894     },
31895
31896     // private
31897     beforeResize : function(){
31898         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
31899     },
31900
31901     // private
31902     onResize : function(){
31903         this.refreshSize();
31904         this.syncBodyHeight();
31905         this.adjustAssets();
31906         this.focus();
31907         this.fireEvent("resize", this, this.size.width, this.size.height);
31908     },
31909
31910     // private
31911     onKeyDown : function(e){
31912         if(this.isVisible()){
31913             this.fireEvent("keydown", this, e);
31914         }
31915     },
31916
31917     /**
31918      * Resizes the dialog.
31919      * @param {Number} width
31920      * @param {Number} height
31921      * @return {Roo.BasicDialog} this
31922      */
31923     resizeTo : function(width, height){
31924         this.el.setSize(width, height);
31925         this.size = {width: width, height: height};
31926         this.syncBodyHeight();
31927         if(this.fixedcenter){
31928             this.center();
31929         }
31930         if(this.isVisible()){
31931             this.constrainXY();
31932             this.adjustAssets();
31933         }
31934         this.fireEvent("resize", this, width, height);
31935         return this;
31936     },
31937
31938
31939     /**
31940      * Resizes the dialog to fit the specified content size.
31941      * @param {Number} width
31942      * @param {Number} height
31943      * @return {Roo.BasicDialog} this
31944      */
31945     setContentSize : function(w, h){
31946         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
31947         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
31948         //if(!this.el.isBorderBox()){
31949             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
31950             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
31951         //}
31952         if(this.tabs){
31953             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
31954             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
31955         }
31956         this.resizeTo(w, h);
31957         return this;
31958     },
31959
31960     /**
31961      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
31962      * executed in response to a particular key being pressed while the dialog is active.
31963      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
31964      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
31965      * @param {Function} fn The function to call
31966      * @param {Object} scope (optional) The scope of the function
31967      * @return {Roo.BasicDialog} this
31968      */
31969     addKeyListener : function(key, fn, scope){
31970         var keyCode, shift, ctrl, alt;
31971         if(typeof key == "object" && !(key instanceof Array)){
31972             keyCode = key["key"];
31973             shift = key["shift"];
31974             ctrl = key["ctrl"];
31975             alt = key["alt"];
31976         }else{
31977             keyCode = key;
31978         }
31979         var handler = function(dlg, e){
31980             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
31981                 var k = e.getKey();
31982                 if(keyCode instanceof Array){
31983                     for(var i = 0, len = keyCode.length; i < len; i++){
31984                         if(keyCode[i] == k){
31985                           fn.call(scope || window, dlg, k, e);
31986                           return;
31987                         }
31988                     }
31989                 }else{
31990                     if(k == keyCode){
31991                         fn.call(scope || window, dlg, k, e);
31992                     }
31993                 }
31994             }
31995         };
31996         this.on("keydown", handler);
31997         return this;
31998     },
31999
32000     /**
32001      * Returns the TabPanel component (creates it if it doesn't exist).
32002      * Note: If you wish to simply check for the existence of tabs without creating them,
32003      * check for a null 'tabs' property.
32004      * @return {Roo.TabPanel} The tabs component
32005      */
32006     getTabs : function(){
32007         if(!this.tabs){
32008             this.el.addClass("x-dlg-auto-tabs");
32009             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32010             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32011         }
32012         return this.tabs;
32013     },
32014
32015     /**
32016      * Adds a button to the footer section of the dialog.
32017      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32018      * object or a valid Roo.DomHelper element config
32019      * @param {Function} handler The function called when the button is clicked
32020      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32021      * @return {Roo.Button} The new button
32022      */
32023     addButton : function(config, handler, scope){
32024         var dh = Roo.DomHelper;
32025         if(!this.footer){
32026             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32027         }
32028         if(!this.btnContainer){
32029             var tb = this.footer.createChild({
32030
32031                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32032                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32033             }, null, true);
32034             this.btnContainer = tb.firstChild.firstChild.firstChild;
32035         }
32036         var bconfig = {
32037             handler: handler,
32038             scope: scope,
32039             minWidth: this.minButtonWidth,
32040             hideParent:true
32041         };
32042         if(typeof config == "string"){
32043             bconfig.text = config;
32044         }else{
32045             if(config.tag){
32046                 bconfig.dhconfig = config;
32047             }else{
32048                 Roo.apply(bconfig, config);
32049             }
32050         }
32051         var fc = false;
32052         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32053             bconfig.position = Math.max(0, bconfig.position);
32054             fc = this.btnContainer.childNodes[bconfig.position];
32055         }
32056          
32057         var btn = new Roo.Button(
32058             fc ? 
32059                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32060                 : this.btnContainer.appendChild(document.createElement("td")),
32061             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32062             bconfig
32063         );
32064         this.syncBodyHeight();
32065         if(!this.buttons){
32066             /**
32067              * Array of all the buttons that have been added to this dialog via addButton
32068              * @type Array
32069              */
32070             this.buttons = [];
32071         }
32072         this.buttons.push(btn);
32073         return btn;
32074     },
32075
32076     /**
32077      * Sets the default button to be focused when the dialog is displayed.
32078      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32079      * @return {Roo.BasicDialog} this
32080      */
32081     setDefaultButton : function(btn){
32082         this.defaultButton = btn;
32083         return this;
32084     },
32085
32086     // private
32087     getHeaderFooterHeight : function(safe){
32088         var height = 0;
32089         if(this.header){
32090            height += this.header.getHeight();
32091         }
32092         if(this.footer){
32093            var fm = this.footer.getMargins();
32094             height += (this.footer.getHeight()+fm.top+fm.bottom);
32095         }
32096         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32097         height += this.centerBg.getPadding("tb");
32098         return height;
32099     },
32100
32101     // private
32102     syncBodyHeight : function()
32103     {
32104         var bd = this.body, // the text
32105             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32106             bw = this.bwrap;
32107         var height = this.size.height - this.getHeaderFooterHeight(false);
32108         bd.setHeight(height-bd.getMargins("tb"));
32109         var hh = this.header.getHeight();
32110         var h = this.size.height-hh;
32111         cb.setHeight(h);
32112         
32113         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32114         bw.setHeight(h-cb.getPadding("tb"));
32115         
32116         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32117         bd.setWidth(bw.getWidth(true));
32118         if(this.tabs){
32119             this.tabs.syncHeight();
32120             if(Roo.isIE){
32121                 this.tabs.el.repaint();
32122             }
32123         }
32124     },
32125
32126     /**
32127      * Restores the previous state of the dialog if Roo.state is configured.
32128      * @return {Roo.BasicDialog} this
32129      */
32130     restoreState : function(){
32131         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32132         if(box && box.width){
32133             this.xy = [box.x, box.y];
32134             this.resizeTo(box.width, box.height);
32135         }
32136         return this;
32137     },
32138
32139     // private
32140     beforeShow : function(){
32141         this.expand();
32142         if(this.fixedcenter){
32143             this.xy = this.el.getCenterXY(true);
32144         }
32145         if(this.modal){
32146             Roo.get(document.body).addClass("x-body-masked");
32147             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32148             this.mask.show();
32149         }
32150         this.constrainXY();
32151     },
32152
32153     // private
32154     animShow : function(){
32155         var b = Roo.get(this.animateTarget).getBox();
32156         this.proxy.setSize(b.width, b.height);
32157         this.proxy.setLocation(b.x, b.y);
32158         this.proxy.show();
32159         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32160                     true, .35, this.showEl.createDelegate(this));
32161     },
32162
32163     /**
32164      * Shows the dialog.
32165      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32166      * @return {Roo.BasicDialog} this
32167      */
32168     show : function(animateTarget){
32169         if (this.fireEvent("beforeshow", this) === false){
32170             return;
32171         }
32172         if(this.syncHeightBeforeShow){
32173             this.syncBodyHeight();
32174         }else if(this.firstShow){
32175             this.firstShow = false;
32176             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32177         }
32178         this.animateTarget = animateTarget || this.animateTarget;
32179         if(!this.el.isVisible()){
32180             this.beforeShow();
32181             if(this.animateTarget && Roo.get(this.animateTarget)){
32182                 this.animShow();
32183             }else{
32184                 this.showEl();
32185             }
32186         }
32187         return this;
32188     },
32189
32190     // private
32191     showEl : function(){
32192         this.proxy.hide();
32193         this.el.setXY(this.xy);
32194         this.el.show();
32195         this.adjustAssets(true);
32196         this.toFront();
32197         this.focus();
32198         // IE peekaboo bug - fix found by Dave Fenwick
32199         if(Roo.isIE){
32200             this.el.repaint();
32201         }
32202         this.fireEvent("show", this);
32203     },
32204
32205     /**
32206      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32207      * dialog itself will receive focus.
32208      */
32209     focus : function(){
32210         if(this.defaultButton){
32211             this.defaultButton.focus();
32212         }else{
32213             this.focusEl.focus();
32214         }
32215     },
32216
32217     // private
32218     constrainXY : function(){
32219         if(this.constraintoviewport !== false){
32220             if(!this.viewSize){
32221                 if(this.container){
32222                     var s = this.container.getSize();
32223                     this.viewSize = [s.width, s.height];
32224                 }else{
32225                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32226                 }
32227             }
32228             var s = Roo.get(this.container||document).getScroll();
32229
32230             var x = this.xy[0], y = this.xy[1];
32231             var w = this.size.width, h = this.size.height;
32232             var vw = this.viewSize[0], vh = this.viewSize[1];
32233             // only move it if it needs it
32234             var moved = false;
32235             // first validate right/bottom
32236             if(x + w > vw+s.left){
32237                 x = vw - w;
32238                 moved = true;
32239             }
32240             if(y + h > vh+s.top){
32241                 y = vh - h;
32242                 moved = true;
32243             }
32244             // then make sure top/left isn't negative
32245             if(x < s.left){
32246                 x = s.left;
32247                 moved = true;
32248             }
32249             if(y < s.top){
32250                 y = s.top;
32251                 moved = true;
32252             }
32253             if(moved){
32254                 // cache xy
32255                 this.xy = [x, y];
32256                 if(this.isVisible()){
32257                     this.el.setLocation(x, y);
32258                     this.adjustAssets();
32259                 }
32260             }
32261         }
32262     },
32263
32264     // private
32265     onDrag : function(){
32266         if(!this.proxyDrag){
32267             this.xy = this.el.getXY();
32268             this.adjustAssets();
32269         }
32270     },
32271
32272     // private
32273     adjustAssets : function(doShow){
32274         var x = this.xy[0], y = this.xy[1];
32275         var w = this.size.width, h = this.size.height;
32276         if(doShow === true){
32277             if(this.shadow){
32278                 this.shadow.show(this.el);
32279             }
32280             if(this.shim){
32281                 this.shim.show();
32282             }
32283         }
32284         if(this.shadow && this.shadow.isVisible()){
32285             this.shadow.show(this.el);
32286         }
32287         if(this.shim && this.shim.isVisible()){
32288             this.shim.setBounds(x, y, w, h);
32289         }
32290     },
32291
32292     // private
32293     adjustViewport : function(w, h){
32294         if(!w || !h){
32295             w = Roo.lib.Dom.getViewWidth();
32296             h = Roo.lib.Dom.getViewHeight();
32297         }
32298         // cache the size
32299         this.viewSize = [w, h];
32300         if(this.modal && this.mask.isVisible()){
32301             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32302             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32303         }
32304         if(this.isVisible()){
32305             this.constrainXY();
32306         }
32307     },
32308
32309     /**
32310      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32311      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32312      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32313      */
32314     destroy : function(removeEl){
32315         if(this.isVisible()){
32316             this.animateTarget = null;
32317             this.hide();
32318         }
32319         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32320         if(this.tabs){
32321             this.tabs.destroy(removeEl);
32322         }
32323         Roo.destroy(
32324              this.shim,
32325              this.proxy,
32326              this.resizer,
32327              this.close,
32328              this.mask
32329         );
32330         if(this.dd){
32331             this.dd.unreg();
32332         }
32333         if(this.buttons){
32334            for(var i = 0, len = this.buttons.length; i < len; i++){
32335                this.buttons[i].destroy();
32336            }
32337         }
32338         this.el.removeAllListeners();
32339         if(removeEl === true){
32340             this.el.update("");
32341             this.el.remove();
32342         }
32343         Roo.DialogManager.unregister(this);
32344     },
32345
32346     // private
32347     startMove : function(){
32348         if(this.proxyDrag){
32349             this.proxy.show();
32350         }
32351         if(this.constraintoviewport !== false){
32352             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32353         }
32354     },
32355
32356     // private
32357     endMove : function(){
32358         if(!this.proxyDrag){
32359             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32360         }else{
32361             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32362             this.proxy.hide();
32363         }
32364         this.refreshSize();
32365         this.adjustAssets();
32366         this.focus();
32367         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32368     },
32369
32370     /**
32371      * Brings this dialog to the front of any other visible dialogs
32372      * @return {Roo.BasicDialog} this
32373      */
32374     toFront : function(){
32375         Roo.DialogManager.bringToFront(this);
32376         return this;
32377     },
32378
32379     /**
32380      * Sends this dialog to the back (under) of any other visible dialogs
32381      * @return {Roo.BasicDialog} this
32382      */
32383     toBack : function(){
32384         Roo.DialogManager.sendToBack(this);
32385         return this;
32386     },
32387
32388     /**
32389      * Centers this dialog in the viewport
32390      * @return {Roo.BasicDialog} this
32391      */
32392     center : function(){
32393         var xy = this.el.getCenterXY(true);
32394         this.moveTo(xy[0], xy[1]);
32395         return this;
32396     },
32397
32398     /**
32399      * Moves the dialog's top-left corner to the specified point
32400      * @param {Number} x
32401      * @param {Number} y
32402      * @return {Roo.BasicDialog} this
32403      */
32404     moveTo : function(x, y){
32405         this.xy = [x,y];
32406         if(this.isVisible()){
32407             this.el.setXY(this.xy);
32408             this.adjustAssets();
32409         }
32410         return this;
32411     },
32412
32413     /**
32414      * Aligns the dialog to the specified element
32415      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32416      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32417      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32418      * @return {Roo.BasicDialog} this
32419      */
32420     alignTo : function(element, position, offsets){
32421         this.xy = this.el.getAlignToXY(element, position, offsets);
32422         if(this.isVisible()){
32423             this.el.setXY(this.xy);
32424             this.adjustAssets();
32425         }
32426         return this;
32427     },
32428
32429     /**
32430      * Anchors an element to another element and realigns it when the window is resized.
32431      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32432      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32433      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32434      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32435      * is a number, it is used as the buffer delay (defaults to 50ms).
32436      * @return {Roo.BasicDialog} this
32437      */
32438     anchorTo : function(el, alignment, offsets, monitorScroll){
32439         var action = function(){
32440             this.alignTo(el, alignment, offsets);
32441         };
32442         Roo.EventManager.onWindowResize(action, this);
32443         var tm = typeof monitorScroll;
32444         if(tm != 'undefined'){
32445             Roo.EventManager.on(window, 'scroll', action, this,
32446                 {buffer: tm == 'number' ? monitorScroll : 50});
32447         }
32448         action.call(this);
32449         return this;
32450     },
32451
32452     /**
32453      * Returns true if the dialog is visible
32454      * @return {Boolean}
32455      */
32456     isVisible : function(){
32457         return this.el.isVisible();
32458     },
32459
32460     // private
32461     animHide : function(callback){
32462         var b = Roo.get(this.animateTarget).getBox();
32463         this.proxy.show();
32464         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32465         this.el.hide();
32466         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32467                     this.hideEl.createDelegate(this, [callback]));
32468     },
32469
32470     /**
32471      * Hides the dialog.
32472      * @param {Function} callback (optional) Function to call when the dialog is hidden
32473      * @return {Roo.BasicDialog} this
32474      */
32475     hide : function(callback){
32476         if (this.fireEvent("beforehide", this) === false){
32477             return;
32478         }
32479         if(this.shadow){
32480             this.shadow.hide();
32481         }
32482         if(this.shim) {
32483           this.shim.hide();
32484         }
32485         // sometimes animateTarget seems to get set.. causing problems...
32486         // this just double checks..
32487         if(this.animateTarget && Roo.get(this.animateTarget)) {
32488            this.animHide(callback);
32489         }else{
32490             this.el.hide();
32491             this.hideEl(callback);
32492         }
32493         return this;
32494     },
32495
32496     // private
32497     hideEl : function(callback){
32498         this.proxy.hide();
32499         if(this.modal){
32500             this.mask.hide();
32501             Roo.get(document.body).removeClass("x-body-masked");
32502         }
32503         this.fireEvent("hide", this);
32504         if(typeof callback == "function"){
32505             callback();
32506         }
32507     },
32508
32509     // private
32510     hideAction : function(){
32511         this.setLeft("-10000px");
32512         this.setTop("-10000px");
32513         this.setStyle("visibility", "hidden");
32514     },
32515
32516     // private
32517     refreshSize : function(){
32518         this.size = this.el.getSize();
32519         this.xy = this.el.getXY();
32520         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32521     },
32522
32523     // private
32524     // z-index is managed by the DialogManager and may be overwritten at any time
32525     setZIndex : function(index){
32526         if(this.modal){
32527             this.mask.setStyle("z-index", index);
32528         }
32529         if(this.shim){
32530             this.shim.setStyle("z-index", ++index);
32531         }
32532         if(this.shadow){
32533             this.shadow.setZIndex(++index);
32534         }
32535         this.el.setStyle("z-index", ++index);
32536         if(this.proxy){
32537             this.proxy.setStyle("z-index", ++index);
32538         }
32539         if(this.resizer){
32540             this.resizer.proxy.setStyle("z-index", ++index);
32541         }
32542
32543         this.lastZIndex = index;
32544     },
32545
32546     /**
32547      * Returns the element for this dialog
32548      * @return {Roo.Element} The underlying dialog Element
32549      */
32550     getEl : function(){
32551         return this.el;
32552     }
32553 });
32554
32555 /**
32556  * @class Roo.DialogManager
32557  * Provides global access to BasicDialogs that have been created and
32558  * support for z-indexing (layering) multiple open dialogs.
32559  */
32560 Roo.DialogManager = function(){
32561     var list = {};
32562     var accessList = [];
32563     var front = null;
32564
32565     // private
32566     var sortDialogs = function(d1, d2){
32567         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32568     };
32569
32570     // private
32571     var orderDialogs = function(){
32572         accessList.sort(sortDialogs);
32573         var seed = Roo.DialogManager.zseed;
32574         for(var i = 0, len = accessList.length; i < len; i++){
32575             var dlg = accessList[i];
32576             if(dlg){
32577                 dlg.setZIndex(seed + (i*10));
32578             }
32579         }
32580     };
32581
32582     return {
32583         /**
32584          * The starting z-index for BasicDialogs (defaults to 9000)
32585          * @type Number The z-index value
32586          */
32587         zseed : 9000,
32588
32589         // private
32590         register : function(dlg){
32591             list[dlg.id] = dlg;
32592             accessList.push(dlg);
32593         },
32594
32595         // private
32596         unregister : function(dlg){
32597             delete list[dlg.id];
32598             var i=0;
32599             var len=0;
32600             if(!accessList.indexOf){
32601                 for(  i = 0, len = accessList.length; i < len; i++){
32602                     if(accessList[i] == dlg){
32603                         accessList.splice(i, 1);
32604                         return;
32605                     }
32606                 }
32607             }else{
32608                  i = accessList.indexOf(dlg);
32609                 if(i != -1){
32610                     accessList.splice(i, 1);
32611                 }
32612             }
32613         },
32614
32615         /**
32616          * Gets a registered dialog by id
32617          * @param {String/Object} id The id of the dialog or a dialog
32618          * @return {Roo.BasicDialog} this
32619          */
32620         get : function(id){
32621             return typeof id == "object" ? id : list[id];
32622         },
32623
32624         /**
32625          * Brings the specified dialog to the front
32626          * @param {String/Object} dlg The id of the dialog or a dialog
32627          * @return {Roo.BasicDialog} this
32628          */
32629         bringToFront : function(dlg){
32630             dlg = this.get(dlg);
32631             if(dlg != front){
32632                 front = dlg;
32633                 dlg._lastAccess = new Date().getTime();
32634                 orderDialogs();
32635             }
32636             return dlg;
32637         },
32638
32639         /**
32640          * Sends the specified dialog to the back
32641          * @param {String/Object} dlg The id of the dialog or a dialog
32642          * @return {Roo.BasicDialog} this
32643          */
32644         sendToBack : function(dlg){
32645             dlg = this.get(dlg);
32646             dlg._lastAccess = -(new Date().getTime());
32647             orderDialogs();
32648             return dlg;
32649         },
32650
32651         /**
32652          * Hides all dialogs
32653          */
32654         hideAll : function(){
32655             for(var id in list){
32656                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32657                     list[id].hide();
32658                 }
32659             }
32660         }
32661     };
32662 }();
32663
32664 /**
32665  * @class Roo.LayoutDialog
32666  * @extends Roo.BasicDialog
32667  * Dialog which provides adjustments for working with a layout in a Dialog.
32668  * Add your necessary layout config options to the dialog's config.<br>
32669  * Example usage (including a nested layout):
32670  * <pre><code>
32671 if(!dialog){
32672     dialog = new Roo.LayoutDialog("download-dlg", {
32673         modal: true,
32674         width:600,
32675         height:450,
32676         shadow:true,
32677         minWidth:500,
32678         minHeight:350,
32679         autoTabs:true,
32680         proxyDrag:true,
32681         // layout config merges with the dialog config
32682         center:{
32683             tabPosition: "top",
32684             alwaysShowTabs: true
32685         }
32686     });
32687     dialog.addKeyListener(27, dialog.hide, dialog);
32688     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32689     dialog.addButton("Build It!", this.getDownload, this);
32690
32691     // we can even add nested layouts
32692     var innerLayout = new Roo.BorderLayout("dl-inner", {
32693         east: {
32694             initialSize: 200,
32695             autoScroll:true,
32696             split:true
32697         },
32698         center: {
32699             autoScroll:true
32700         }
32701     });
32702     innerLayout.beginUpdate();
32703     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32704     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32705     innerLayout.endUpdate(true);
32706
32707     var layout = dialog.getLayout();
32708     layout.beginUpdate();
32709     layout.add("center", new Roo.ContentPanel("standard-panel",
32710                         {title: "Download the Source", fitToFrame:true}));
32711     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32712                {title: "Build your own roo.js"}));
32713     layout.getRegion("center").showPanel(sp);
32714     layout.endUpdate();
32715 }
32716 </code></pre>
32717     * @constructor
32718     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32719     * @param {Object} config configuration options
32720   */
32721 Roo.LayoutDialog = function(el, cfg){
32722     
32723     var config=  cfg;
32724     if (typeof(cfg) == 'undefined') {
32725         config = Roo.apply({}, el);
32726         // not sure why we use documentElement here.. - it should always be body.
32727         // IE7 borks horribly if we use documentElement.
32728         // webkit also does not like documentElement - it creates a body element...
32729         el = Roo.get( document.body || document.documentElement ).createChild();
32730         //config.autoCreate = true;
32731     }
32732     
32733     
32734     config.autoTabs = false;
32735     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32736     this.body.setStyle({overflow:"hidden", position:"relative"});
32737     this.layout = new Roo.BorderLayout(this.body.dom, config);
32738     this.layout.monitorWindowResize = false;
32739     this.el.addClass("x-dlg-auto-layout");
32740     // fix case when center region overwrites center function
32741     this.center = Roo.BasicDialog.prototype.center;
32742     this.on("show", this.layout.layout, this.layout, true);
32743     if (config.items) {
32744         var xitems = config.items;
32745         delete config.items;
32746         Roo.each(xitems, this.addxtype, this);
32747     }
32748     
32749     
32750 };
32751 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32752     /**
32753      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32754      * @deprecated
32755      */
32756     endUpdate : function(){
32757         this.layout.endUpdate();
32758     },
32759
32760     /**
32761      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32762      *  @deprecated
32763      */
32764     beginUpdate : function(){
32765         this.layout.beginUpdate();
32766     },
32767
32768     /**
32769      * Get the BorderLayout for this dialog
32770      * @return {Roo.BorderLayout}
32771      */
32772     getLayout : function(){
32773         return this.layout;
32774     },
32775
32776     showEl : function(){
32777         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32778         if(Roo.isIE7){
32779             this.layout.layout();
32780         }
32781     },
32782
32783     // private
32784     // Use the syncHeightBeforeShow config option to control this automatically
32785     syncBodyHeight : function(){
32786         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32787         if(this.layout){this.layout.layout();}
32788     },
32789     
32790       /**
32791      * Add an xtype element (actually adds to the layout.)
32792      * @return {Object} xdata xtype object data.
32793      */
32794     
32795     addxtype : function(c) {
32796         return this.layout.addxtype(c);
32797     }
32798 });/*
32799  * Based on:
32800  * Ext JS Library 1.1.1
32801  * Copyright(c) 2006-2007, Ext JS, LLC.
32802  *
32803  * Originally Released Under LGPL - original licence link has changed is not relivant.
32804  *
32805  * Fork - LGPL
32806  * <script type="text/javascript">
32807  */
32808  
32809 /**
32810  * @class Roo.MessageBox
32811  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32812  * Example usage:
32813  *<pre><code>
32814 // Basic alert:
32815 Roo.Msg.alert('Status', 'Changes saved successfully.');
32816
32817 // Prompt for user data:
32818 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32819     if (btn == 'ok'){
32820         // process text value...
32821     }
32822 });
32823
32824 // Show a dialog using config options:
32825 Roo.Msg.show({
32826    title:'Save Changes?',
32827    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32828    buttons: Roo.Msg.YESNOCANCEL,
32829    fn: processResult,
32830    animEl: 'elId'
32831 });
32832 </code></pre>
32833  * @singleton
32834  */
32835 Roo.MessageBox = function(){
32836     var dlg, opt, mask, waitTimer;
32837     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32838     var buttons, activeTextEl, bwidth;
32839
32840     // private
32841     var handleButton = function(button){
32842         dlg.hide();
32843         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32844     };
32845
32846     // private
32847     var handleHide = function(){
32848         if(opt && opt.cls){
32849             dlg.el.removeClass(opt.cls);
32850         }
32851         if(waitTimer){
32852             Roo.TaskMgr.stop(waitTimer);
32853             waitTimer = null;
32854         }
32855     };
32856
32857     // private
32858     var updateButtons = function(b){
32859         var width = 0;
32860         if(!b){
32861             buttons["ok"].hide();
32862             buttons["cancel"].hide();
32863             buttons["yes"].hide();
32864             buttons["no"].hide();
32865             dlg.footer.dom.style.display = 'none';
32866             return width;
32867         }
32868         dlg.footer.dom.style.display = '';
32869         for(var k in buttons){
32870             if(typeof buttons[k] != "function"){
32871                 if(b[k]){
32872                     buttons[k].show();
32873                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
32874                     width += buttons[k].el.getWidth()+15;
32875                 }else{
32876                     buttons[k].hide();
32877                 }
32878             }
32879         }
32880         return width;
32881     };
32882
32883     // private
32884     var handleEsc = function(d, k, e){
32885         if(opt && opt.closable !== false){
32886             dlg.hide();
32887         }
32888         if(e){
32889             e.stopEvent();
32890         }
32891     };
32892
32893     return {
32894         /**
32895          * Returns a reference to the underlying {@link Roo.BasicDialog} element
32896          * @return {Roo.BasicDialog} The BasicDialog element
32897          */
32898         getDialog : function(){
32899            if(!dlg){
32900                 dlg = new Roo.BasicDialog("x-msg-box", {
32901                     autoCreate : true,
32902                     shadow: true,
32903                     draggable: true,
32904                     resizable:false,
32905                     constraintoviewport:false,
32906                     fixedcenter:true,
32907                     collapsible : false,
32908                     shim:true,
32909                     modal: true,
32910                     width:400, height:100,
32911                     buttonAlign:"center",
32912                     closeClick : function(){
32913                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
32914                             handleButton("no");
32915                         }else{
32916                             handleButton("cancel");
32917                         }
32918                     }
32919                 });
32920                 dlg.on("hide", handleHide);
32921                 mask = dlg.mask;
32922                 dlg.addKeyListener(27, handleEsc);
32923                 buttons = {};
32924                 var bt = this.buttonText;
32925                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
32926                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
32927                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
32928                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
32929                 bodyEl = dlg.body.createChild({
32930
32931                     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>'
32932                 });
32933                 msgEl = bodyEl.dom.firstChild;
32934                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
32935                 textboxEl.enableDisplayMode();
32936                 textboxEl.addKeyListener([10,13], function(){
32937                     if(dlg.isVisible() && opt && opt.buttons){
32938                         if(opt.buttons.ok){
32939                             handleButton("ok");
32940                         }else if(opt.buttons.yes){
32941                             handleButton("yes");
32942                         }
32943                     }
32944                 });
32945                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
32946                 textareaEl.enableDisplayMode();
32947                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
32948                 progressEl.enableDisplayMode();
32949                 var pf = progressEl.dom.firstChild;
32950                 if (pf) {
32951                     pp = Roo.get(pf.firstChild);
32952                     pp.setHeight(pf.offsetHeight);
32953                 }
32954                 
32955             }
32956             return dlg;
32957         },
32958
32959         /**
32960          * Updates the message box body text
32961          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
32962          * the XHTML-compliant non-breaking space character '&amp;#160;')
32963          * @return {Roo.MessageBox} This message box
32964          */
32965         updateText : function(text){
32966             if(!dlg.isVisible() && !opt.width){
32967                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
32968             }
32969             msgEl.innerHTML = text || '&#160;';
32970       
32971             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
32972             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
32973             var w = Math.max(
32974                     Math.min(opt.width || cw , this.maxWidth), 
32975                     Math.max(opt.minWidth || this.minWidth, bwidth)
32976             );
32977             if(opt.prompt){
32978                 activeTextEl.setWidth(w);
32979             }
32980             if(dlg.isVisible()){
32981                 dlg.fixedcenter = false;
32982             }
32983             // to big, make it scroll. = But as usual stupid IE does not support
32984             // !important..
32985             
32986             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
32987                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
32988                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
32989             } else {
32990                 bodyEl.dom.style.height = '';
32991                 bodyEl.dom.style.overflowY = '';
32992             }
32993             if (cw > w) {
32994                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
32995             } else {
32996                 bodyEl.dom.style.overflowX = '';
32997             }
32998             
32999             dlg.setContentSize(w, bodyEl.getHeight());
33000             if(dlg.isVisible()){
33001                 dlg.fixedcenter = true;
33002             }
33003             return this;
33004         },
33005
33006         /**
33007          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33008          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33009          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33010          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33011          * @return {Roo.MessageBox} This message box
33012          */
33013         updateProgress : function(value, text){
33014             if(text){
33015                 this.updateText(text);
33016             }
33017             if (pp) { // weird bug on my firefox - for some reason this is not defined
33018                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33019             }
33020             return this;
33021         },        
33022
33023         /**
33024          * Returns true if the message box is currently displayed
33025          * @return {Boolean} True if the message box is visible, else false
33026          */
33027         isVisible : function(){
33028             return dlg && dlg.isVisible();  
33029         },
33030
33031         /**
33032          * Hides the message box if it is displayed
33033          */
33034         hide : function(){
33035             if(this.isVisible()){
33036                 dlg.hide();
33037             }  
33038         },
33039
33040         /**
33041          * Displays a new message box, or reinitializes an existing message box, based on the config options
33042          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33043          * The following config object properties are supported:
33044          * <pre>
33045 Property    Type             Description
33046 ----------  ---------------  ------------------------------------------------------------------------------------
33047 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33048                                    closes (defaults to undefined)
33049 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33050                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33051 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33052                                    progress and wait dialogs will ignore this property and always hide the
33053                                    close button as they can only be closed programmatically.
33054 cls               String           A custom CSS class to apply to the message box element
33055 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33056                                    displayed (defaults to 75)
33057 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33058                                    function will be btn (the name of the button that was clicked, if applicable,
33059                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33060                                    Progress and wait dialogs will ignore this option since they do not respond to
33061                                    user actions and can only be closed programmatically, so any required function
33062                                    should be called by the same code after it closes the dialog.
33063 icon              String           A CSS class that provides a background image to be used as an icon for
33064                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33065 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33066 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33067 modal             Boolean          False to allow user interaction with the page while the message box is
33068                                    displayed (defaults to true)
33069 msg               String           A string that will replace the existing message box body text (defaults
33070                                    to the XHTML-compliant non-breaking space character '&#160;')
33071 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33072 progress          Boolean          True to display a progress bar (defaults to false)
33073 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33074 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33075 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33076 title             String           The title text
33077 value             String           The string value to set into the active textbox element if displayed
33078 wait              Boolean          True to display a progress bar (defaults to false)
33079 width             Number           The width of the dialog in pixels
33080 </pre>
33081          *
33082          * Example usage:
33083          * <pre><code>
33084 Roo.Msg.show({
33085    title: 'Address',
33086    msg: 'Please enter your address:',
33087    width: 300,
33088    buttons: Roo.MessageBox.OKCANCEL,
33089    multiline: true,
33090    fn: saveAddress,
33091    animEl: 'addAddressBtn'
33092 });
33093 </code></pre>
33094          * @param {Object} config Configuration options
33095          * @return {Roo.MessageBox} This message box
33096          */
33097         show : function(options)
33098         {
33099             
33100             // this causes nightmares if you show one dialog after another
33101             // especially on callbacks..
33102              
33103             if(this.isVisible()){
33104                 
33105                 this.hide();
33106                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33107                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33108                 Roo.log("New Dialog Message:" +  options.msg )
33109                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33110                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33111                 
33112             }
33113             var d = this.getDialog();
33114             opt = options;
33115             d.setTitle(opt.title || "&#160;");
33116             d.close.setDisplayed(opt.closable !== false);
33117             activeTextEl = textboxEl;
33118             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33119             if(opt.prompt){
33120                 if(opt.multiline){
33121                     textboxEl.hide();
33122                     textareaEl.show();
33123                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33124                         opt.multiline : this.defaultTextHeight);
33125                     activeTextEl = textareaEl;
33126                 }else{
33127                     textboxEl.show();
33128                     textareaEl.hide();
33129                 }
33130             }else{
33131                 textboxEl.hide();
33132                 textareaEl.hide();
33133             }
33134             progressEl.setDisplayed(opt.progress === true);
33135             this.updateProgress(0);
33136             activeTextEl.dom.value = opt.value || "";
33137             if(opt.prompt){
33138                 dlg.setDefaultButton(activeTextEl);
33139             }else{
33140                 var bs = opt.buttons;
33141                 var db = null;
33142                 if(bs && bs.ok){
33143                     db = buttons["ok"];
33144                 }else if(bs && bs.yes){
33145                     db = buttons["yes"];
33146                 }
33147                 dlg.setDefaultButton(db);
33148             }
33149             bwidth = updateButtons(opt.buttons);
33150             this.updateText(opt.msg);
33151             if(opt.cls){
33152                 d.el.addClass(opt.cls);
33153             }
33154             d.proxyDrag = opt.proxyDrag === true;
33155             d.modal = opt.modal !== false;
33156             d.mask = opt.modal !== false ? mask : false;
33157             if(!d.isVisible()){
33158                 // force it to the end of the z-index stack so it gets a cursor in FF
33159                 document.body.appendChild(dlg.el.dom);
33160                 d.animateTarget = null;
33161                 d.show(options.animEl);
33162             }
33163             return this;
33164         },
33165
33166         /**
33167          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33168          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33169          * and closing the message box when the process is complete.
33170          * @param {String} title The title bar text
33171          * @param {String} msg The message box body text
33172          * @return {Roo.MessageBox} This message box
33173          */
33174         progress : function(title, msg){
33175             this.show({
33176                 title : title,
33177                 msg : msg,
33178                 buttons: false,
33179                 progress:true,
33180                 closable:false,
33181                 minWidth: this.minProgressWidth,
33182                 modal : true
33183             });
33184             return this;
33185         },
33186
33187         /**
33188          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33189          * If a callback function is passed it will be called after the user clicks the button, and the
33190          * id of the button that was clicked will be passed as the only parameter to the callback
33191          * (could also be the top-right close button).
33192          * @param {String} title The title bar text
33193          * @param {String} msg The message box body text
33194          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33195          * @param {Object} scope (optional) The scope of the callback function
33196          * @return {Roo.MessageBox} This message box
33197          */
33198         alert : function(title, msg, fn, scope){
33199             this.show({
33200                 title : title,
33201                 msg : msg,
33202                 buttons: this.OK,
33203                 fn: fn,
33204                 scope : scope,
33205                 modal : true
33206             });
33207             return this;
33208         },
33209
33210         /**
33211          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33212          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33213          * You are responsible for closing the message box when the process is complete.
33214          * @param {String} msg The message box body text
33215          * @param {String} title (optional) The title bar text
33216          * @return {Roo.MessageBox} This message box
33217          */
33218         wait : function(msg, title){
33219             this.show({
33220                 title : title,
33221                 msg : msg,
33222                 buttons: false,
33223                 closable:false,
33224                 progress:true,
33225                 modal:true,
33226                 width:300,
33227                 wait:true
33228             });
33229             waitTimer = Roo.TaskMgr.start({
33230                 run: function(i){
33231                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33232                 },
33233                 interval: 1000
33234             });
33235             return this;
33236         },
33237
33238         /**
33239          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33240          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33241          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33242          * @param {String} title The title bar text
33243          * @param {String} msg The message box body text
33244          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33245          * @param {Object} scope (optional) The scope of the callback function
33246          * @return {Roo.MessageBox} This message box
33247          */
33248         confirm : function(title, msg, fn, scope){
33249             this.show({
33250                 title : title,
33251                 msg : msg,
33252                 buttons: this.YESNO,
33253                 fn: fn,
33254                 scope : scope,
33255                 modal : true
33256             });
33257             return this;
33258         },
33259
33260         /**
33261          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33262          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33263          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33264          * (could also be the top-right close button) and the text that was entered will be passed as the two
33265          * parameters to the callback.
33266          * @param {String} title The title bar text
33267          * @param {String} msg The message box body text
33268          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33269          * @param {Object} scope (optional) The scope of the callback function
33270          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33271          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33272          * @return {Roo.MessageBox} This message box
33273          */
33274         prompt : function(title, msg, fn, scope, multiline){
33275             this.show({
33276                 title : title,
33277                 msg : msg,
33278                 buttons: this.OKCANCEL,
33279                 fn: fn,
33280                 minWidth:250,
33281                 scope : scope,
33282                 prompt:true,
33283                 multiline: multiline,
33284                 modal : true
33285             });
33286             return this;
33287         },
33288
33289         /**
33290          * Button config that displays a single OK button
33291          * @type Object
33292          */
33293         OK : {ok:true},
33294         /**
33295          * Button config that displays Yes and No buttons
33296          * @type Object
33297          */
33298         YESNO : {yes:true, no:true},
33299         /**
33300          * Button config that displays OK and Cancel buttons
33301          * @type Object
33302          */
33303         OKCANCEL : {ok:true, cancel:true},
33304         /**
33305          * Button config that displays Yes, No and Cancel buttons
33306          * @type Object
33307          */
33308         YESNOCANCEL : {yes:true, no:true, cancel:true},
33309
33310         /**
33311          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33312          * @type Number
33313          */
33314         defaultTextHeight : 75,
33315         /**
33316          * The maximum width in pixels of the message box (defaults to 600)
33317          * @type Number
33318          */
33319         maxWidth : 600,
33320         /**
33321          * The minimum width in pixels of the message box (defaults to 100)
33322          * @type Number
33323          */
33324         minWidth : 100,
33325         /**
33326          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33327          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33328          * @type Number
33329          */
33330         minProgressWidth : 250,
33331         /**
33332          * An object containing the default button text strings that can be overriden for localized language support.
33333          * Supported properties are: ok, cancel, yes and no.
33334          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33335          * @type Object
33336          */
33337         buttonText : {
33338             ok : "OK",
33339             cancel : "Cancel",
33340             yes : "Yes",
33341             no : "No"
33342         }
33343     };
33344 }();
33345
33346 /**
33347  * Shorthand for {@link Roo.MessageBox}
33348  */
33349 Roo.Msg = Roo.MessageBox;/*
33350  * Based on:
33351  * Ext JS Library 1.1.1
33352  * Copyright(c) 2006-2007, Ext JS, LLC.
33353  *
33354  * Originally Released Under LGPL - original licence link has changed is not relivant.
33355  *
33356  * Fork - LGPL
33357  * <script type="text/javascript">
33358  */
33359 /**
33360  * @class Roo.QuickTips
33361  * Provides attractive and customizable tooltips for any element.
33362  * @singleton
33363  */
33364 Roo.QuickTips = function(){
33365     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33366     var ce, bd, xy, dd;
33367     var visible = false, disabled = true, inited = false;
33368     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33369     
33370     var onOver = function(e){
33371         if(disabled){
33372             return;
33373         }
33374         var t = e.getTarget();
33375         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33376             return;
33377         }
33378         if(ce && t == ce.el){
33379             clearTimeout(hideProc);
33380             return;
33381         }
33382         if(t && tagEls[t.id]){
33383             tagEls[t.id].el = t;
33384             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33385             return;
33386         }
33387         var ttp, et = Roo.fly(t);
33388         var ns = cfg.namespace;
33389         if(tm.interceptTitles && t.title){
33390             ttp = t.title;
33391             t.qtip = ttp;
33392             t.removeAttribute("title");
33393             e.preventDefault();
33394         }else{
33395             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33396         }
33397         if(ttp){
33398             showProc = show.defer(tm.showDelay, tm, [{
33399                 el: t, 
33400                 text: ttp, 
33401                 width: et.getAttributeNS(ns, cfg.width),
33402                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33403                 title: et.getAttributeNS(ns, cfg.title),
33404                     cls: et.getAttributeNS(ns, cfg.cls)
33405             }]);
33406         }
33407     };
33408     
33409     var onOut = function(e){
33410         clearTimeout(showProc);
33411         var t = e.getTarget();
33412         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33413             hideProc = setTimeout(hide, tm.hideDelay);
33414         }
33415     };
33416     
33417     var onMove = function(e){
33418         if(disabled){
33419             return;
33420         }
33421         xy = e.getXY();
33422         xy[1] += 18;
33423         if(tm.trackMouse && ce){
33424             el.setXY(xy);
33425         }
33426     };
33427     
33428     var onDown = function(e){
33429         clearTimeout(showProc);
33430         clearTimeout(hideProc);
33431         if(!e.within(el)){
33432             if(tm.hideOnClick){
33433                 hide();
33434                 tm.disable();
33435                 tm.enable.defer(100, tm);
33436             }
33437         }
33438     };
33439     
33440     var getPad = function(){
33441         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33442     };
33443
33444     var show = function(o){
33445         if(disabled){
33446             return;
33447         }
33448         clearTimeout(dismissProc);
33449         ce = o;
33450         if(removeCls){ // in case manually hidden
33451             el.removeClass(removeCls);
33452             removeCls = null;
33453         }
33454         if(ce.cls){
33455             el.addClass(ce.cls);
33456             removeCls = ce.cls;
33457         }
33458         if(ce.title){
33459             tipTitle.update(ce.title);
33460             tipTitle.show();
33461         }else{
33462             tipTitle.update('');
33463             tipTitle.hide();
33464         }
33465         el.dom.style.width  = tm.maxWidth+'px';
33466         //tipBody.dom.style.width = '';
33467         tipBodyText.update(o.text);
33468         var p = getPad(), w = ce.width;
33469         if(!w){
33470             var td = tipBodyText.dom;
33471             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33472             if(aw > tm.maxWidth){
33473                 w = tm.maxWidth;
33474             }else if(aw < tm.minWidth){
33475                 w = tm.minWidth;
33476             }else{
33477                 w = aw;
33478             }
33479         }
33480         //tipBody.setWidth(w);
33481         el.setWidth(parseInt(w, 10) + p);
33482         if(ce.autoHide === false){
33483             close.setDisplayed(true);
33484             if(dd){
33485                 dd.unlock();
33486             }
33487         }else{
33488             close.setDisplayed(false);
33489             if(dd){
33490                 dd.lock();
33491             }
33492         }
33493         if(xy){
33494             el.avoidY = xy[1]-18;
33495             el.setXY(xy);
33496         }
33497         if(tm.animate){
33498             el.setOpacity(.1);
33499             el.setStyle("visibility", "visible");
33500             el.fadeIn({callback: afterShow});
33501         }else{
33502             afterShow();
33503         }
33504     };
33505     
33506     var afterShow = function(){
33507         if(ce){
33508             el.show();
33509             esc.enable();
33510             if(tm.autoDismiss && ce.autoHide !== false){
33511                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33512             }
33513         }
33514     };
33515     
33516     var hide = function(noanim){
33517         clearTimeout(dismissProc);
33518         clearTimeout(hideProc);
33519         ce = null;
33520         if(el.isVisible()){
33521             esc.disable();
33522             if(noanim !== true && tm.animate){
33523                 el.fadeOut({callback: afterHide});
33524             }else{
33525                 afterHide();
33526             } 
33527         }
33528     };
33529     
33530     var afterHide = function(){
33531         el.hide();
33532         if(removeCls){
33533             el.removeClass(removeCls);
33534             removeCls = null;
33535         }
33536     };
33537     
33538     return {
33539         /**
33540         * @cfg {Number} minWidth
33541         * The minimum width of the quick tip (defaults to 40)
33542         */
33543        minWidth : 40,
33544         /**
33545         * @cfg {Number} maxWidth
33546         * The maximum width of the quick tip (defaults to 300)
33547         */
33548        maxWidth : 300,
33549         /**
33550         * @cfg {Boolean} interceptTitles
33551         * True to automatically use the element's DOM title value if available (defaults to false)
33552         */
33553        interceptTitles : false,
33554         /**
33555         * @cfg {Boolean} trackMouse
33556         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33557         */
33558        trackMouse : false,
33559         /**
33560         * @cfg {Boolean} hideOnClick
33561         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33562         */
33563        hideOnClick : true,
33564         /**
33565         * @cfg {Number} showDelay
33566         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33567         */
33568        showDelay : 500,
33569         /**
33570         * @cfg {Number} hideDelay
33571         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33572         */
33573        hideDelay : 200,
33574         /**
33575         * @cfg {Boolean} autoHide
33576         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33577         * Used in conjunction with hideDelay.
33578         */
33579        autoHide : true,
33580         /**
33581         * @cfg {Boolean}
33582         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33583         * (defaults to true).  Used in conjunction with autoDismissDelay.
33584         */
33585        autoDismiss : true,
33586         /**
33587         * @cfg {Number}
33588         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33589         */
33590        autoDismissDelay : 5000,
33591        /**
33592         * @cfg {Boolean} animate
33593         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33594         */
33595        animate : false,
33596
33597        /**
33598         * @cfg {String} title
33599         * Title text to display (defaults to '').  This can be any valid HTML markup.
33600         */
33601         title: '',
33602        /**
33603         * @cfg {String} text
33604         * Body text to display (defaults to '').  This can be any valid HTML markup.
33605         */
33606         text : '',
33607        /**
33608         * @cfg {String} cls
33609         * A CSS class to apply to the base quick tip element (defaults to '').
33610         */
33611         cls : '',
33612        /**
33613         * @cfg {Number} width
33614         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33615         * minWidth or maxWidth.
33616         */
33617         width : null,
33618
33619     /**
33620      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33621      * or display QuickTips in a page.
33622      */
33623        init : function(){
33624           tm = Roo.QuickTips;
33625           cfg = tm.tagConfig;
33626           if(!inited){
33627               if(!Roo.isReady){ // allow calling of init() before onReady
33628                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33629                   return;
33630               }
33631               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33632               el.fxDefaults = {stopFx: true};
33633               // maximum custom styling
33634               //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>');
33635               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>');              
33636               tipTitle = el.child('h3');
33637               tipTitle.enableDisplayMode("block");
33638               tipBody = el.child('div.x-tip-bd');
33639               tipBodyText = el.child('div.x-tip-bd-inner');
33640               //bdLeft = el.child('div.x-tip-bd-left');
33641               //bdRight = el.child('div.x-tip-bd-right');
33642               close = el.child('div.x-tip-close');
33643               close.enableDisplayMode("block");
33644               close.on("click", hide);
33645               var d = Roo.get(document);
33646               d.on("mousedown", onDown);
33647               d.on("mouseover", onOver);
33648               d.on("mouseout", onOut);
33649               d.on("mousemove", onMove);
33650               esc = d.addKeyListener(27, hide);
33651               esc.disable();
33652               if(Roo.dd.DD){
33653                   dd = el.initDD("default", null, {
33654                       onDrag : function(){
33655                           el.sync();  
33656                       }
33657                   });
33658                   dd.setHandleElId(tipTitle.id);
33659                   dd.lock();
33660               }
33661               inited = true;
33662           }
33663           this.enable(); 
33664        },
33665
33666     /**
33667      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33668      * are supported:
33669      * <pre>
33670 Property    Type                   Description
33671 ----------  ---------------------  ------------------------------------------------------------------------
33672 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33673      * </ul>
33674      * @param {Object} config The config object
33675      */
33676        register : function(config){
33677            var cs = config instanceof Array ? config : arguments;
33678            for(var i = 0, len = cs.length; i < len; i++) {
33679                var c = cs[i];
33680                var target = c.target;
33681                if(target){
33682                    if(target instanceof Array){
33683                        for(var j = 0, jlen = target.length; j < jlen; j++){
33684                            tagEls[target[j]] = c;
33685                        }
33686                    }else{
33687                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33688                    }
33689                }
33690            }
33691        },
33692
33693     /**
33694      * Removes this quick tip from its element and destroys it.
33695      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33696      */
33697        unregister : function(el){
33698            delete tagEls[Roo.id(el)];
33699        },
33700
33701     /**
33702      * Enable this quick tip.
33703      */
33704        enable : function(){
33705            if(inited && disabled){
33706                locks.pop();
33707                if(locks.length < 1){
33708                    disabled = false;
33709                }
33710            }
33711        },
33712
33713     /**
33714      * Disable this quick tip.
33715      */
33716        disable : function(){
33717           disabled = true;
33718           clearTimeout(showProc);
33719           clearTimeout(hideProc);
33720           clearTimeout(dismissProc);
33721           if(ce){
33722               hide(true);
33723           }
33724           locks.push(1);
33725        },
33726
33727     /**
33728      * Returns true if the quick tip is enabled, else false.
33729      */
33730        isEnabled : function(){
33731             return !disabled;
33732        },
33733
33734         // private
33735        tagConfig : {
33736            namespace : "roo", // was ext?? this may break..
33737            alt_namespace : "ext",
33738            attribute : "qtip",
33739            width : "width",
33740            target : "target",
33741            title : "qtitle",
33742            hide : "hide",
33743            cls : "qclass"
33744        }
33745    };
33746 }();
33747
33748 // backwards compat
33749 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33750  * Based on:
33751  * Ext JS Library 1.1.1
33752  * Copyright(c) 2006-2007, Ext JS, LLC.
33753  *
33754  * Originally Released Under LGPL - original licence link has changed is not relivant.
33755  *
33756  * Fork - LGPL
33757  * <script type="text/javascript">
33758  */
33759  
33760
33761 /**
33762  * @class Roo.tree.TreePanel
33763  * @extends Roo.data.Tree
33764
33765  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33766  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33767  * @cfg {Boolean} enableDD true to enable drag and drop
33768  * @cfg {Boolean} enableDrag true to enable just drag
33769  * @cfg {Boolean} enableDrop true to enable just drop
33770  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33771  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33772  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33773  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33774  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33775  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33776  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33777  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33778  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33779  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33780  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33781  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33782  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33783  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33784  * @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>
33785  * @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>
33786  * 
33787  * @constructor
33788  * @param {String/HTMLElement/Element} el The container element
33789  * @param {Object} config
33790  */
33791 Roo.tree.TreePanel = function(el, config){
33792     var root = false;
33793     var loader = false;
33794     if (config.root) {
33795         root = config.root;
33796         delete config.root;
33797     }
33798     if (config.loader) {
33799         loader = config.loader;
33800         delete config.loader;
33801     }
33802     
33803     Roo.apply(this, config);
33804     Roo.tree.TreePanel.superclass.constructor.call(this);
33805     this.el = Roo.get(el);
33806     this.el.addClass('x-tree');
33807     //console.log(root);
33808     if (root) {
33809         this.setRootNode( Roo.factory(root, Roo.tree));
33810     }
33811     if (loader) {
33812         this.loader = Roo.factory(loader, Roo.tree);
33813     }
33814    /**
33815     * Read-only. The id of the container element becomes this TreePanel's id.
33816     */
33817     this.id = this.el.id;
33818     this.addEvents({
33819         /**
33820         * @event beforeload
33821         * Fires before a node is loaded, return false to cancel
33822         * @param {Node} node The node being loaded
33823         */
33824         "beforeload" : true,
33825         /**
33826         * @event load
33827         * Fires when a node is loaded
33828         * @param {Node} node The node that was loaded
33829         */
33830         "load" : true,
33831         /**
33832         * @event textchange
33833         * Fires when the text for a node is changed
33834         * @param {Node} node The node
33835         * @param {String} text The new text
33836         * @param {String} oldText The old text
33837         */
33838         "textchange" : true,
33839         /**
33840         * @event beforeexpand
33841         * Fires before a node is expanded, return false to cancel.
33842         * @param {Node} node The node
33843         * @param {Boolean} deep
33844         * @param {Boolean} anim
33845         */
33846         "beforeexpand" : true,
33847         /**
33848         * @event beforecollapse
33849         * Fires before a node is collapsed, return false to cancel.
33850         * @param {Node} node The node
33851         * @param {Boolean} deep
33852         * @param {Boolean} anim
33853         */
33854         "beforecollapse" : true,
33855         /**
33856         * @event expand
33857         * Fires when a node is expanded
33858         * @param {Node} node The node
33859         */
33860         "expand" : true,
33861         /**
33862         * @event disabledchange
33863         * Fires when the disabled status of a node changes
33864         * @param {Node} node The node
33865         * @param {Boolean} disabled
33866         */
33867         "disabledchange" : true,
33868         /**
33869         * @event collapse
33870         * Fires when a node is collapsed
33871         * @param {Node} node The node
33872         */
33873         "collapse" : true,
33874         /**
33875         * @event beforeclick
33876         * Fires before click processing on a node. Return false to cancel the default action.
33877         * @param {Node} node The node
33878         * @param {Roo.EventObject} e The event object
33879         */
33880         "beforeclick":true,
33881         /**
33882         * @event checkchange
33883         * Fires when a node with a checkbox's checked property changes
33884         * @param {Node} this This node
33885         * @param {Boolean} checked
33886         */
33887         "checkchange":true,
33888         /**
33889         * @event click
33890         * Fires when a node is clicked
33891         * @param {Node} node The node
33892         * @param {Roo.EventObject} e The event object
33893         */
33894         "click":true,
33895         /**
33896         * @event dblclick
33897         * Fires when a node is double clicked
33898         * @param {Node} node The node
33899         * @param {Roo.EventObject} e The event object
33900         */
33901         "dblclick":true,
33902         /**
33903         * @event contextmenu
33904         * Fires when a node is right clicked
33905         * @param {Node} node The node
33906         * @param {Roo.EventObject} e The event object
33907         */
33908         "contextmenu":true,
33909         /**
33910         * @event beforechildrenrendered
33911         * Fires right before the child nodes for a node are rendered
33912         * @param {Node} node The node
33913         */
33914         "beforechildrenrendered":true,
33915         /**
33916         * @event startdrag
33917         * Fires when a node starts being dragged
33918         * @param {Roo.tree.TreePanel} this
33919         * @param {Roo.tree.TreeNode} node
33920         * @param {event} e The raw browser event
33921         */ 
33922        "startdrag" : true,
33923        /**
33924         * @event enddrag
33925         * Fires when a drag operation is complete
33926         * @param {Roo.tree.TreePanel} this
33927         * @param {Roo.tree.TreeNode} node
33928         * @param {event} e The raw browser event
33929         */
33930        "enddrag" : true,
33931        /**
33932         * @event dragdrop
33933         * Fires when a dragged node is dropped on a valid DD target
33934         * @param {Roo.tree.TreePanel} this
33935         * @param {Roo.tree.TreeNode} node
33936         * @param {DD} dd The dd it was dropped on
33937         * @param {event} e The raw browser event
33938         */
33939        "dragdrop" : true,
33940        /**
33941         * @event beforenodedrop
33942         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
33943         * passed to handlers has the following properties:<br />
33944         * <ul style="padding:5px;padding-left:16px;">
33945         * <li>tree - The TreePanel</li>
33946         * <li>target - The node being targeted for the drop</li>
33947         * <li>data - The drag data from the drag source</li>
33948         * <li>point - The point of the drop - append, above or below</li>
33949         * <li>source - The drag source</li>
33950         * <li>rawEvent - Raw mouse event</li>
33951         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
33952         * to be inserted by setting them on this object.</li>
33953         * <li>cancel - Set this to true to cancel the drop.</li>
33954         * </ul>
33955         * @param {Object} dropEvent
33956         */
33957        "beforenodedrop" : true,
33958        /**
33959         * @event nodedrop
33960         * Fires after a DD object is dropped on a node in this tree. The dropEvent
33961         * passed to handlers has the following properties:<br />
33962         * <ul style="padding:5px;padding-left:16px;">
33963         * <li>tree - The TreePanel</li>
33964         * <li>target - The node being targeted for the drop</li>
33965         * <li>data - The drag data from the drag source</li>
33966         * <li>point - The point of the drop - append, above or below</li>
33967         * <li>source - The drag source</li>
33968         * <li>rawEvent - Raw mouse event</li>
33969         * <li>dropNode - Dropped node(s).</li>
33970         * </ul>
33971         * @param {Object} dropEvent
33972         */
33973        "nodedrop" : true,
33974         /**
33975         * @event nodedragover
33976         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
33977         * passed to handlers has the following properties:<br />
33978         * <ul style="padding:5px;padding-left:16px;">
33979         * <li>tree - The TreePanel</li>
33980         * <li>target - The node being targeted for the drop</li>
33981         * <li>data - The drag data from the drag source</li>
33982         * <li>point - The point of the drop - append, above or below</li>
33983         * <li>source - The drag source</li>
33984         * <li>rawEvent - Raw mouse event</li>
33985         * <li>dropNode - Drop node(s) provided by the source.</li>
33986         * <li>cancel - Set this to true to signal drop not allowed.</li>
33987         * </ul>
33988         * @param {Object} dragOverEvent
33989         */
33990        "nodedragover" : true
33991         
33992     });
33993     if(this.singleExpand){
33994        this.on("beforeexpand", this.restrictExpand, this);
33995     }
33996     if (this.editor) {
33997         this.editor.tree = this;
33998         this.editor = Roo.factory(this.editor, Roo.tree);
33999     }
34000     
34001     if (this.selModel) {
34002         this.selModel = Roo.factory(this.selModel, Roo.tree);
34003     }
34004    
34005 };
34006 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34007     rootVisible : true,
34008     animate: Roo.enableFx,
34009     lines : true,
34010     enableDD : false,
34011     hlDrop : Roo.enableFx,
34012   
34013     renderer: false,
34014     
34015     rendererTip: false,
34016     // private
34017     restrictExpand : function(node){
34018         var p = node.parentNode;
34019         if(p){
34020             if(p.expandedChild && p.expandedChild.parentNode == p){
34021                 p.expandedChild.collapse();
34022             }
34023             p.expandedChild = node;
34024         }
34025     },
34026
34027     // private override
34028     setRootNode : function(node){
34029         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34030         if(!this.rootVisible){
34031             node.ui = new Roo.tree.RootTreeNodeUI(node);
34032         }
34033         return node;
34034     },
34035
34036     /**
34037      * Returns the container element for this TreePanel
34038      */
34039     getEl : function(){
34040         return this.el;
34041     },
34042
34043     /**
34044      * Returns the default TreeLoader for this TreePanel
34045      */
34046     getLoader : function(){
34047         return this.loader;
34048     },
34049
34050     /**
34051      * Expand all nodes
34052      */
34053     expandAll : function(){
34054         this.root.expand(true);
34055     },
34056
34057     /**
34058      * Collapse all nodes
34059      */
34060     collapseAll : function(){
34061         this.root.collapse(true);
34062     },
34063
34064     /**
34065      * Returns the selection model used by this TreePanel
34066      */
34067     getSelectionModel : function(){
34068         if(!this.selModel){
34069             this.selModel = new Roo.tree.DefaultSelectionModel();
34070         }
34071         return this.selModel;
34072     },
34073
34074     /**
34075      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34076      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34077      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34078      * @return {Array}
34079      */
34080     getChecked : function(a, startNode){
34081         startNode = startNode || this.root;
34082         var r = [];
34083         var f = function(){
34084             if(this.attributes.checked){
34085                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34086             }
34087         }
34088         startNode.cascade(f);
34089         return r;
34090     },
34091
34092     /**
34093      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34094      * @param {String} path
34095      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34096      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34097      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34098      */
34099     expandPath : function(path, attr, callback){
34100         attr = attr || "id";
34101         var keys = path.split(this.pathSeparator);
34102         var curNode = this.root;
34103         if(curNode.attributes[attr] != keys[1]){ // invalid root
34104             if(callback){
34105                 callback(false, null);
34106             }
34107             return;
34108         }
34109         var index = 1;
34110         var f = function(){
34111             if(++index == keys.length){
34112                 if(callback){
34113                     callback(true, curNode);
34114                 }
34115                 return;
34116             }
34117             var c = curNode.findChild(attr, keys[index]);
34118             if(!c){
34119                 if(callback){
34120                     callback(false, curNode);
34121                 }
34122                 return;
34123             }
34124             curNode = c;
34125             c.expand(false, false, f);
34126         };
34127         curNode.expand(false, false, f);
34128     },
34129
34130     /**
34131      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34132      * @param {String} path
34133      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34134      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34135      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34136      */
34137     selectPath : function(path, attr, callback){
34138         attr = attr || "id";
34139         var keys = path.split(this.pathSeparator);
34140         var v = keys.pop();
34141         if(keys.length > 0){
34142             var f = function(success, node){
34143                 if(success && node){
34144                     var n = node.findChild(attr, v);
34145                     if(n){
34146                         n.select();
34147                         if(callback){
34148                             callback(true, n);
34149                         }
34150                     }else if(callback){
34151                         callback(false, n);
34152                     }
34153                 }else{
34154                     if(callback){
34155                         callback(false, n);
34156                     }
34157                 }
34158             };
34159             this.expandPath(keys.join(this.pathSeparator), attr, f);
34160         }else{
34161             this.root.select();
34162             if(callback){
34163                 callback(true, this.root);
34164             }
34165         }
34166     },
34167
34168     getTreeEl : function(){
34169         return this.el;
34170     },
34171
34172     /**
34173      * Trigger rendering of this TreePanel
34174      */
34175     render : function(){
34176         if (this.innerCt) {
34177             return this; // stop it rendering more than once!!
34178         }
34179         
34180         this.innerCt = this.el.createChild({tag:"ul",
34181                cls:"x-tree-root-ct " +
34182                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34183
34184         if(this.containerScroll){
34185             Roo.dd.ScrollManager.register(this.el);
34186         }
34187         if((this.enableDD || this.enableDrop) && !this.dropZone){
34188            /**
34189             * The dropZone used by this tree if drop is enabled
34190             * @type Roo.tree.TreeDropZone
34191             */
34192              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34193                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34194            });
34195         }
34196         if((this.enableDD || this.enableDrag) && !this.dragZone){
34197            /**
34198             * The dragZone used by this tree if drag is enabled
34199             * @type Roo.tree.TreeDragZone
34200             */
34201             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34202                ddGroup: this.ddGroup || "TreeDD",
34203                scroll: this.ddScroll
34204            });
34205         }
34206         this.getSelectionModel().init(this);
34207         if (!this.root) {
34208             Roo.log("ROOT not set in tree");
34209             return this;
34210         }
34211         this.root.render();
34212         if(!this.rootVisible){
34213             this.root.renderChildren();
34214         }
34215         return this;
34216     }
34217 });/*
34218  * Based on:
34219  * Ext JS Library 1.1.1
34220  * Copyright(c) 2006-2007, Ext JS, LLC.
34221  *
34222  * Originally Released Under LGPL - original licence link has changed is not relivant.
34223  *
34224  * Fork - LGPL
34225  * <script type="text/javascript">
34226  */
34227  
34228
34229 /**
34230  * @class Roo.tree.DefaultSelectionModel
34231  * @extends Roo.util.Observable
34232  * The default single selection for a TreePanel.
34233  * @param {Object} cfg Configuration
34234  */
34235 Roo.tree.DefaultSelectionModel = function(cfg){
34236    this.selNode = null;
34237    
34238    
34239    
34240    this.addEvents({
34241        /**
34242         * @event selectionchange
34243         * Fires when the selected node changes
34244         * @param {DefaultSelectionModel} this
34245         * @param {TreeNode} node the new selection
34246         */
34247        "selectionchange" : true,
34248
34249        /**
34250         * @event beforeselect
34251         * Fires before the selected node changes, return false to cancel the change
34252         * @param {DefaultSelectionModel} this
34253         * @param {TreeNode} node the new selection
34254         * @param {TreeNode} node the old selection
34255         */
34256        "beforeselect" : true
34257    });
34258    
34259     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34260 };
34261
34262 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34263     init : function(tree){
34264         this.tree = tree;
34265         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34266         tree.on("click", this.onNodeClick, this);
34267     },
34268     
34269     onNodeClick : function(node, e){
34270         if (e.ctrlKey && this.selNode == node)  {
34271             this.unselect(node);
34272             return;
34273         }
34274         this.select(node);
34275     },
34276     
34277     /**
34278      * Select a node.
34279      * @param {TreeNode} node The node to select
34280      * @return {TreeNode} The selected node
34281      */
34282     select : function(node){
34283         var last = this.selNode;
34284         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34285             if(last){
34286                 last.ui.onSelectedChange(false);
34287             }
34288             this.selNode = node;
34289             node.ui.onSelectedChange(true);
34290             this.fireEvent("selectionchange", this, node, last);
34291         }
34292         return node;
34293     },
34294     
34295     /**
34296      * Deselect a node.
34297      * @param {TreeNode} node The node to unselect
34298      */
34299     unselect : function(node){
34300         if(this.selNode == node){
34301             this.clearSelections();
34302         }    
34303     },
34304     
34305     /**
34306      * Clear all selections
34307      */
34308     clearSelections : function(){
34309         var n = this.selNode;
34310         if(n){
34311             n.ui.onSelectedChange(false);
34312             this.selNode = null;
34313             this.fireEvent("selectionchange", this, null);
34314         }
34315         return n;
34316     },
34317     
34318     /**
34319      * Get the selected node
34320      * @return {TreeNode} The selected node
34321      */
34322     getSelectedNode : function(){
34323         return this.selNode;    
34324     },
34325     
34326     /**
34327      * Returns true if the node is selected
34328      * @param {TreeNode} node The node to check
34329      * @return {Boolean}
34330      */
34331     isSelected : function(node){
34332         return this.selNode == node;  
34333     },
34334
34335     /**
34336      * Selects the node above the selected node in the tree, intelligently walking the nodes
34337      * @return TreeNode The new selection
34338      */
34339     selectPrevious : function(){
34340         var s = this.selNode || this.lastSelNode;
34341         if(!s){
34342             return null;
34343         }
34344         var ps = s.previousSibling;
34345         if(ps){
34346             if(!ps.isExpanded() || ps.childNodes.length < 1){
34347                 return this.select(ps);
34348             } else{
34349                 var lc = ps.lastChild;
34350                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34351                     lc = lc.lastChild;
34352                 }
34353                 return this.select(lc);
34354             }
34355         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34356             return this.select(s.parentNode);
34357         }
34358         return null;
34359     },
34360
34361     /**
34362      * Selects the node above the selected node in the tree, intelligently walking the nodes
34363      * @return TreeNode The new selection
34364      */
34365     selectNext : function(){
34366         var s = this.selNode || this.lastSelNode;
34367         if(!s){
34368             return null;
34369         }
34370         if(s.firstChild && s.isExpanded()){
34371              return this.select(s.firstChild);
34372          }else if(s.nextSibling){
34373              return this.select(s.nextSibling);
34374          }else if(s.parentNode){
34375             var newS = null;
34376             s.parentNode.bubble(function(){
34377                 if(this.nextSibling){
34378                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34379                     return false;
34380                 }
34381             });
34382             return newS;
34383          }
34384         return null;
34385     },
34386
34387     onKeyDown : function(e){
34388         var s = this.selNode || this.lastSelNode;
34389         // undesirable, but required
34390         var sm = this;
34391         if(!s){
34392             return;
34393         }
34394         var k = e.getKey();
34395         switch(k){
34396              case e.DOWN:
34397                  e.stopEvent();
34398                  this.selectNext();
34399              break;
34400              case e.UP:
34401                  e.stopEvent();
34402                  this.selectPrevious();
34403              break;
34404              case e.RIGHT:
34405                  e.preventDefault();
34406                  if(s.hasChildNodes()){
34407                      if(!s.isExpanded()){
34408                          s.expand();
34409                      }else if(s.firstChild){
34410                          this.select(s.firstChild, e);
34411                      }
34412                  }
34413              break;
34414              case e.LEFT:
34415                  e.preventDefault();
34416                  if(s.hasChildNodes() && s.isExpanded()){
34417                      s.collapse();
34418                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34419                      this.select(s.parentNode, e);
34420                  }
34421              break;
34422         };
34423     }
34424 });
34425
34426 /**
34427  * @class Roo.tree.MultiSelectionModel
34428  * @extends Roo.util.Observable
34429  * Multi selection for a TreePanel.
34430  * @param {Object} cfg Configuration
34431  */
34432 Roo.tree.MultiSelectionModel = function(){
34433    this.selNodes = [];
34434    this.selMap = {};
34435    this.addEvents({
34436        /**
34437         * @event selectionchange
34438         * Fires when the selected nodes change
34439         * @param {MultiSelectionModel} this
34440         * @param {Array} nodes Array of the selected nodes
34441         */
34442        "selectionchange" : true
34443    });
34444    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34445    
34446 };
34447
34448 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34449     init : function(tree){
34450         this.tree = tree;
34451         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34452         tree.on("click", this.onNodeClick, this);
34453     },
34454     
34455     onNodeClick : function(node, e){
34456         this.select(node, e, e.ctrlKey);
34457     },
34458     
34459     /**
34460      * Select a node.
34461      * @param {TreeNode} node The node to select
34462      * @param {EventObject} e (optional) An event associated with the selection
34463      * @param {Boolean} keepExisting True to retain existing selections
34464      * @return {TreeNode} The selected node
34465      */
34466     select : function(node, e, keepExisting){
34467         if(keepExisting !== true){
34468             this.clearSelections(true);
34469         }
34470         if(this.isSelected(node)){
34471             this.lastSelNode = node;
34472             return node;
34473         }
34474         this.selNodes.push(node);
34475         this.selMap[node.id] = node;
34476         this.lastSelNode = node;
34477         node.ui.onSelectedChange(true);
34478         this.fireEvent("selectionchange", this, this.selNodes);
34479         return node;
34480     },
34481     
34482     /**
34483      * Deselect a node.
34484      * @param {TreeNode} node The node to unselect
34485      */
34486     unselect : function(node){
34487         if(this.selMap[node.id]){
34488             node.ui.onSelectedChange(false);
34489             var sn = this.selNodes;
34490             var index = -1;
34491             if(sn.indexOf){
34492                 index = sn.indexOf(node);
34493             }else{
34494                 for(var i = 0, len = sn.length; i < len; i++){
34495                     if(sn[i] == node){
34496                         index = i;
34497                         break;
34498                     }
34499                 }
34500             }
34501             if(index != -1){
34502                 this.selNodes.splice(index, 1);
34503             }
34504             delete this.selMap[node.id];
34505             this.fireEvent("selectionchange", this, this.selNodes);
34506         }
34507     },
34508     
34509     /**
34510      * Clear all selections
34511      */
34512     clearSelections : function(suppressEvent){
34513         var sn = this.selNodes;
34514         if(sn.length > 0){
34515             for(var i = 0, len = sn.length; i < len; i++){
34516                 sn[i].ui.onSelectedChange(false);
34517             }
34518             this.selNodes = [];
34519             this.selMap = {};
34520             if(suppressEvent !== true){
34521                 this.fireEvent("selectionchange", this, this.selNodes);
34522             }
34523         }
34524     },
34525     
34526     /**
34527      * Returns true if the node is selected
34528      * @param {TreeNode} node The node to check
34529      * @return {Boolean}
34530      */
34531     isSelected : function(node){
34532         return this.selMap[node.id] ? true : false;  
34533     },
34534     
34535     /**
34536      * Returns an array of the selected nodes
34537      * @return {Array}
34538      */
34539     getSelectedNodes : function(){
34540         return this.selNodes;    
34541     },
34542
34543     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34544
34545     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34546
34547     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34548 });/*
34549  * Based on:
34550  * Ext JS Library 1.1.1
34551  * Copyright(c) 2006-2007, Ext JS, LLC.
34552  *
34553  * Originally Released Under LGPL - original licence link has changed is not relivant.
34554  *
34555  * Fork - LGPL
34556  * <script type="text/javascript">
34557  */
34558  
34559 /**
34560  * @class Roo.tree.TreeNode
34561  * @extends Roo.data.Node
34562  * @cfg {String} text The text for this node
34563  * @cfg {Boolean} expanded true to start the node expanded
34564  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34565  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34566  * @cfg {Boolean} disabled true to start the node disabled
34567  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34568  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34569  * @cfg {String} cls A css class to be added to the node
34570  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34571  * @cfg {String} href URL of the link used for the node (defaults to #)
34572  * @cfg {String} hrefTarget target frame for the link
34573  * @cfg {String} qtip An Ext QuickTip for the node
34574  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34575  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34576  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34577  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34578  * (defaults to undefined with no checkbox rendered)
34579  * @constructor
34580  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34581  */
34582 Roo.tree.TreeNode = function(attributes){
34583     attributes = attributes || {};
34584     if(typeof attributes == "string"){
34585         attributes = {text: attributes};
34586     }
34587     this.childrenRendered = false;
34588     this.rendered = false;
34589     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34590     this.expanded = attributes.expanded === true;
34591     this.isTarget = attributes.isTarget !== false;
34592     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34593     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34594
34595     /**
34596      * Read-only. The text for this node. To change it use setText().
34597      * @type String
34598      */
34599     this.text = attributes.text;
34600     /**
34601      * True if this node is disabled.
34602      * @type Boolean
34603      */
34604     this.disabled = attributes.disabled === true;
34605
34606     this.addEvents({
34607         /**
34608         * @event textchange
34609         * Fires when the text for this node is changed
34610         * @param {Node} this This node
34611         * @param {String} text The new text
34612         * @param {String} oldText The old text
34613         */
34614         "textchange" : true,
34615         /**
34616         * @event beforeexpand
34617         * Fires before this node is expanded, return false to cancel.
34618         * @param {Node} this This node
34619         * @param {Boolean} deep
34620         * @param {Boolean} anim
34621         */
34622         "beforeexpand" : true,
34623         /**
34624         * @event beforecollapse
34625         * Fires before this node is collapsed, return false to cancel.
34626         * @param {Node} this This node
34627         * @param {Boolean} deep
34628         * @param {Boolean} anim
34629         */
34630         "beforecollapse" : true,
34631         /**
34632         * @event expand
34633         * Fires when this node is expanded
34634         * @param {Node} this This node
34635         */
34636         "expand" : true,
34637         /**
34638         * @event disabledchange
34639         * Fires when the disabled status of this node changes
34640         * @param {Node} this This node
34641         * @param {Boolean} disabled
34642         */
34643         "disabledchange" : true,
34644         /**
34645         * @event collapse
34646         * Fires when this node is collapsed
34647         * @param {Node} this This node
34648         */
34649         "collapse" : true,
34650         /**
34651         * @event beforeclick
34652         * Fires before click processing. Return false to cancel the default action.
34653         * @param {Node} this This node
34654         * @param {Roo.EventObject} e The event object
34655         */
34656         "beforeclick":true,
34657         /**
34658         * @event checkchange
34659         * Fires when a node with a checkbox's checked property changes
34660         * @param {Node} this This node
34661         * @param {Boolean} checked
34662         */
34663         "checkchange":true,
34664         /**
34665         * @event click
34666         * Fires when this node is clicked
34667         * @param {Node} this This node
34668         * @param {Roo.EventObject} e The event object
34669         */
34670         "click":true,
34671         /**
34672         * @event dblclick
34673         * Fires when this node is double clicked
34674         * @param {Node} this This node
34675         * @param {Roo.EventObject} e The event object
34676         */
34677         "dblclick":true,
34678         /**
34679         * @event contextmenu
34680         * Fires when this node is right clicked
34681         * @param {Node} this This node
34682         * @param {Roo.EventObject} e The event object
34683         */
34684         "contextmenu":true,
34685         /**
34686         * @event beforechildrenrendered
34687         * Fires right before the child nodes for this node are rendered
34688         * @param {Node} this This node
34689         */
34690         "beforechildrenrendered":true
34691     });
34692
34693     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34694
34695     /**
34696      * Read-only. The UI for this node
34697      * @type TreeNodeUI
34698      */
34699     this.ui = new uiClass(this);
34700     
34701     // finally support items[]
34702     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34703         return;
34704     }
34705     
34706     
34707     Roo.each(this.attributes.items, function(c) {
34708         this.appendChild(Roo.factory(c,Roo.Tree));
34709     }, this);
34710     delete this.attributes.items;
34711     
34712     
34713     
34714 };
34715 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34716     preventHScroll: true,
34717     /**
34718      * Returns true if this node is expanded
34719      * @return {Boolean}
34720      */
34721     isExpanded : function(){
34722         return this.expanded;
34723     },
34724
34725     /**
34726      * Returns the UI object for this node
34727      * @return {TreeNodeUI}
34728      */
34729     getUI : function(){
34730         return this.ui;
34731     },
34732
34733     // private override
34734     setFirstChild : function(node){
34735         var of = this.firstChild;
34736         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34737         if(this.childrenRendered && of && node != of){
34738             of.renderIndent(true, true);
34739         }
34740         if(this.rendered){
34741             this.renderIndent(true, true);
34742         }
34743     },
34744
34745     // private override
34746     setLastChild : function(node){
34747         var ol = this.lastChild;
34748         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34749         if(this.childrenRendered && ol && node != ol){
34750             ol.renderIndent(true, true);
34751         }
34752         if(this.rendered){
34753             this.renderIndent(true, true);
34754         }
34755     },
34756
34757     // these methods are overridden to provide lazy rendering support
34758     // private override
34759     appendChild : function()
34760     {
34761         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34762         if(node && this.childrenRendered){
34763             node.render();
34764         }
34765         this.ui.updateExpandIcon();
34766         return node;
34767     },
34768
34769     // private override
34770     removeChild : function(node){
34771         this.ownerTree.getSelectionModel().unselect(node);
34772         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34773         // if it's been rendered remove dom node
34774         if(this.childrenRendered){
34775             node.ui.remove();
34776         }
34777         if(this.childNodes.length < 1){
34778             this.collapse(false, false);
34779         }else{
34780             this.ui.updateExpandIcon();
34781         }
34782         if(!this.firstChild) {
34783             this.childrenRendered = false;
34784         }
34785         return node;
34786     },
34787
34788     // private override
34789     insertBefore : function(node, refNode){
34790         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34791         if(newNode && refNode && this.childrenRendered){
34792             node.render();
34793         }
34794         this.ui.updateExpandIcon();
34795         return newNode;
34796     },
34797
34798     /**
34799      * Sets the text for this node
34800      * @param {String} text
34801      */
34802     setText : function(text){
34803         var oldText = this.text;
34804         this.text = text;
34805         this.attributes.text = text;
34806         if(this.rendered){ // event without subscribing
34807             this.ui.onTextChange(this, text, oldText);
34808         }
34809         this.fireEvent("textchange", this, text, oldText);
34810     },
34811
34812     /**
34813      * Triggers selection of this node
34814      */
34815     select : function(){
34816         this.getOwnerTree().getSelectionModel().select(this);
34817     },
34818
34819     /**
34820      * Triggers deselection of this node
34821      */
34822     unselect : function(){
34823         this.getOwnerTree().getSelectionModel().unselect(this);
34824     },
34825
34826     /**
34827      * Returns true if this node is selected
34828      * @return {Boolean}
34829      */
34830     isSelected : function(){
34831         return this.getOwnerTree().getSelectionModel().isSelected(this);
34832     },
34833
34834     /**
34835      * Expand this node.
34836      * @param {Boolean} deep (optional) True to expand all children as well
34837      * @param {Boolean} anim (optional) false to cancel the default animation
34838      * @param {Function} callback (optional) A callback to be called when
34839      * expanding this node completes (does not wait for deep expand to complete).
34840      * Called with 1 parameter, this node.
34841      */
34842     expand : function(deep, anim, callback){
34843         if(!this.expanded){
34844             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34845                 return;
34846             }
34847             if(!this.childrenRendered){
34848                 this.renderChildren();
34849             }
34850             this.expanded = true;
34851             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34852                 this.ui.animExpand(function(){
34853                     this.fireEvent("expand", this);
34854                     if(typeof callback == "function"){
34855                         callback(this);
34856                     }
34857                     if(deep === true){
34858                         this.expandChildNodes(true);
34859                     }
34860                 }.createDelegate(this));
34861                 return;
34862             }else{
34863                 this.ui.expand();
34864                 this.fireEvent("expand", this);
34865                 if(typeof callback == "function"){
34866                     callback(this);
34867                 }
34868             }
34869         }else{
34870            if(typeof callback == "function"){
34871                callback(this);
34872            }
34873         }
34874         if(deep === true){
34875             this.expandChildNodes(true);
34876         }
34877     },
34878
34879     isHiddenRoot : function(){
34880         return this.isRoot && !this.getOwnerTree().rootVisible;
34881     },
34882
34883     /**
34884      * Collapse this node.
34885      * @param {Boolean} deep (optional) True to collapse all children as well
34886      * @param {Boolean} anim (optional) false to cancel the default animation
34887      */
34888     collapse : function(deep, anim){
34889         if(this.expanded && !this.isHiddenRoot()){
34890             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
34891                 return;
34892             }
34893             this.expanded = false;
34894             if((this.getOwnerTree().animate && anim !== false) || anim){
34895                 this.ui.animCollapse(function(){
34896                     this.fireEvent("collapse", this);
34897                     if(deep === true){
34898                         this.collapseChildNodes(true);
34899                     }
34900                 }.createDelegate(this));
34901                 return;
34902             }else{
34903                 this.ui.collapse();
34904                 this.fireEvent("collapse", this);
34905             }
34906         }
34907         if(deep === true){
34908             var cs = this.childNodes;
34909             for(var i = 0, len = cs.length; i < len; i++) {
34910                 cs[i].collapse(true, false);
34911             }
34912         }
34913     },
34914
34915     // private
34916     delayedExpand : function(delay){
34917         if(!this.expandProcId){
34918             this.expandProcId = this.expand.defer(delay, this);
34919         }
34920     },
34921
34922     // private
34923     cancelExpand : function(){
34924         if(this.expandProcId){
34925             clearTimeout(this.expandProcId);
34926         }
34927         this.expandProcId = false;
34928     },
34929
34930     /**
34931      * Toggles expanded/collapsed state of the node
34932      */
34933     toggle : function(){
34934         if(this.expanded){
34935             this.collapse();
34936         }else{
34937             this.expand();
34938         }
34939     },
34940
34941     /**
34942      * Ensures all parent nodes are expanded
34943      */
34944     ensureVisible : function(callback){
34945         var tree = this.getOwnerTree();
34946         tree.expandPath(this.parentNode.getPath(), false, function(){
34947             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
34948             Roo.callback(callback);
34949         }.createDelegate(this));
34950     },
34951
34952     /**
34953      * Expand all child nodes
34954      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
34955      */
34956     expandChildNodes : function(deep){
34957         var cs = this.childNodes;
34958         for(var i = 0, len = cs.length; i < len; i++) {
34959                 cs[i].expand(deep);
34960         }
34961     },
34962
34963     /**
34964      * Collapse all child nodes
34965      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
34966      */
34967     collapseChildNodes : function(deep){
34968         var cs = this.childNodes;
34969         for(var i = 0, len = cs.length; i < len; i++) {
34970                 cs[i].collapse(deep);
34971         }
34972     },
34973
34974     /**
34975      * Disables this node
34976      */
34977     disable : function(){
34978         this.disabled = true;
34979         this.unselect();
34980         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
34981             this.ui.onDisableChange(this, true);
34982         }
34983         this.fireEvent("disabledchange", this, true);
34984     },
34985
34986     /**
34987      * Enables this node
34988      */
34989     enable : function(){
34990         this.disabled = false;
34991         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
34992             this.ui.onDisableChange(this, false);
34993         }
34994         this.fireEvent("disabledchange", this, false);
34995     },
34996
34997     // private
34998     renderChildren : function(suppressEvent){
34999         if(suppressEvent !== false){
35000             this.fireEvent("beforechildrenrendered", this);
35001         }
35002         var cs = this.childNodes;
35003         for(var i = 0, len = cs.length; i < len; i++){
35004             cs[i].render(true);
35005         }
35006         this.childrenRendered = true;
35007     },
35008
35009     // private
35010     sort : function(fn, scope){
35011         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35012         if(this.childrenRendered){
35013             var cs = this.childNodes;
35014             for(var i = 0, len = cs.length; i < len; i++){
35015                 cs[i].render(true);
35016             }
35017         }
35018     },
35019
35020     // private
35021     render : function(bulkRender){
35022         this.ui.render(bulkRender);
35023         if(!this.rendered){
35024             this.rendered = true;
35025             if(this.expanded){
35026                 this.expanded = false;
35027                 this.expand(false, false);
35028             }
35029         }
35030     },
35031
35032     // private
35033     renderIndent : function(deep, refresh){
35034         if(refresh){
35035             this.ui.childIndent = null;
35036         }
35037         this.ui.renderIndent();
35038         if(deep === true && this.childrenRendered){
35039             var cs = this.childNodes;
35040             for(var i = 0, len = cs.length; i < len; i++){
35041                 cs[i].renderIndent(true, refresh);
35042             }
35043         }
35044     }
35045 });/*
35046  * Based on:
35047  * Ext JS Library 1.1.1
35048  * Copyright(c) 2006-2007, Ext JS, LLC.
35049  *
35050  * Originally Released Under LGPL - original licence link has changed is not relivant.
35051  *
35052  * Fork - LGPL
35053  * <script type="text/javascript">
35054  */
35055  
35056 /**
35057  * @class Roo.tree.AsyncTreeNode
35058  * @extends Roo.tree.TreeNode
35059  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35060  * @constructor
35061  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35062  */
35063  Roo.tree.AsyncTreeNode = function(config){
35064     this.loaded = false;
35065     this.loading = false;
35066     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35067     /**
35068     * @event beforeload
35069     * Fires before this node is loaded, return false to cancel
35070     * @param {Node} this This node
35071     */
35072     this.addEvents({'beforeload':true, 'load': true});
35073     /**
35074     * @event load
35075     * Fires when this node is loaded
35076     * @param {Node} this This node
35077     */
35078     /**
35079      * The loader used by this node (defaults to using the tree's defined loader)
35080      * @type TreeLoader
35081      * @property loader
35082      */
35083 };
35084 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35085     expand : function(deep, anim, callback){
35086         if(this.loading){ // if an async load is already running, waiting til it's done
35087             var timer;
35088             var f = function(){
35089                 if(!this.loading){ // done loading
35090                     clearInterval(timer);
35091                     this.expand(deep, anim, callback);
35092                 }
35093             }.createDelegate(this);
35094             timer = setInterval(f, 200);
35095             return;
35096         }
35097         if(!this.loaded){
35098             if(this.fireEvent("beforeload", this) === false){
35099                 return;
35100             }
35101             this.loading = true;
35102             this.ui.beforeLoad(this);
35103             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35104             if(loader){
35105                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35106                 return;
35107             }
35108         }
35109         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35110     },
35111     
35112     /**
35113      * Returns true if this node is currently loading
35114      * @return {Boolean}
35115      */
35116     isLoading : function(){
35117         return this.loading;  
35118     },
35119     
35120     loadComplete : function(deep, anim, callback){
35121         this.loading = false;
35122         this.loaded = true;
35123         this.ui.afterLoad(this);
35124         this.fireEvent("load", this);
35125         this.expand(deep, anim, callback);
35126     },
35127     
35128     /**
35129      * Returns true if this node has been loaded
35130      * @return {Boolean}
35131      */
35132     isLoaded : function(){
35133         return this.loaded;
35134     },
35135     
35136     hasChildNodes : function(){
35137         if(!this.isLeaf() && !this.loaded){
35138             return true;
35139         }else{
35140             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35141         }
35142     },
35143
35144     /**
35145      * Trigger a reload for this node
35146      * @param {Function} callback
35147      */
35148     reload : function(callback){
35149         this.collapse(false, false);
35150         while(this.firstChild){
35151             this.removeChild(this.firstChild);
35152         }
35153         this.childrenRendered = false;
35154         this.loaded = false;
35155         if(this.isHiddenRoot()){
35156             this.expanded = false;
35157         }
35158         this.expand(false, false, callback);
35159     }
35160 });/*
35161  * Based on:
35162  * Ext JS Library 1.1.1
35163  * Copyright(c) 2006-2007, Ext JS, LLC.
35164  *
35165  * Originally Released Under LGPL - original licence link has changed is not relivant.
35166  *
35167  * Fork - LGPL
35168  * <script type="text/javascript">
35169  */
35170  
35171 /**
35172  * @class Roo.tree.TreeNodeUI
35173  * @constructor
35174  * @param {Object} node The node to render
35175  * The TreeNode UI implementation is separate from the
35176  * tree implementation. Unless you are customizing the tree UI,
35177  * you should never have to use this directly.
35178  */
35179 Roo.tree.TreeNodeUI = function(node){
35180     this.node = node;
35181     this.rendered = false;
35182     this.animating = false;
35183     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35184 };
35185
35186 Roo.tree.TreeNodeUI.prototype = {
35187     removeChild : function(node){
35188         if(this.rendered){
35189             this.ctNode.removeChild(node.ui.getEl());
35190         }
35191     },
35192
35193     beforeLoad : function(){
35194          this.addClass("x-tree-node-loading");
35195     },
35196
35197     afterLoad : function(){
35198          this.removeClass("x-tree-node-loading");
35199     },
35200
35201     onTextChange : function(node, text, oldText){
35202         if(this.rendered){
35203             this.textNode.innerHTML = text;
35204         }
35205     },
35206
35207     onDisableChange : function(node, state){
35208         this.disabled = state;
35209         if(state){
35210             this.addClass("x-tree-node-disabled");
35211         }else{
35212             this.removeClass("x-tree-node-disabled");
35213         }
35214     },
35215
35216     onSelectedChange : function(state){
35217         if(state){
35218             this.focus();
35219             this.addClass("x-tree-selected");
35220         }else{
35221             //this.blur();
35222             this.removeClass("x-tree-selected");
35223         }
35224     },
35225
35226     onMove : function(tree, node, oldParent, newParent, index, refNode){
35227         this.childIndent = null;
35228         if(this.rendered){
35229             var targetNode = newParent.ui.getContainer();
35230             if(!targetNode){//target not rendered
35231                 this.holder = document.createElement("div");
35232                 this.holder.appendChild(this.wrap);
35233                 return;
35234             }
35235             var insertBefore = refNode ? refNode.ui.getEl() : null;
35236             if(insertBefore){
35237                 targetNode.insertBefore(this.wrap, insertBefore);
35238             }else{
35239                 targetNode.appendChild(this.wrap);
35240             }
35241             this.node.renderIndent(true);
35242         }
35243     },
35244
35245     addClass : function(cls){
35246         if(this.elNode){
35247             Roo.fly(this.elNode).addClass(cls);
35248         }
35249     },
35250
35251     removeClass : function(cls){
35252         if(this.elNode){
35253             Roo.fly(this.elNode).removeClass(cls);
35254         }
35255     },
35256
35257     remove : function(){
35258         if(this.rendered){
35259             this.holder = document.createElement("div");
35260             this.holder.appendChild(this.wrap);
35261         }
35262     },
35263
35264     fireEvent : function(){
35265         return this.node.fireEvent.apply(this.node, arguments);
35266     },
35267
35268     initEvents : function(){
35269         this.node.on("move", this.onMove, this);
35270         var E = Roo.EventManager;
35271         var a = this.anchor;
35272
35273         var el = Roo.fly(a, '_treeui');
35274
35275         if(Roo.isOpera){ // opera render bug ignores the CSS
35276             el.setStyle("text-decoration", "none");
35277         }
35278
35279         el.on("click", this.onClick, this);
35280         el.on("dblclick", this.onDblClick, this);
35281
35282         if(this.checkbox){
35283             Roo.EventManager.on(this.checkbox,
35284                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35285         }
35286
35287         el.on("contextmenu", this.onContextMenu, this);
35288
35289         var icon = Roo.fly(this.iconNode);
35290         icon.on("click", this.onClick, this);
35291         icon.on("dblclick", this.onDblClick, this);
35292         icon.on("contextmenu", this.onContextMenu, this);
35293         E.on(this.ecNode, "click", this.ecClick, this, true);
35294
35295         if(this.node.disabled){
35296             this.addClass("x-tree-node-disabled");
35297         }
35298         if(this.node.hidden){
35299             this.addClass("x-tree-node-disabled");
35300         }
35301         var ot = this.node.getOwnerTree();
35302         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35303         if(dd && (!this.node.isRoot || ot.rootVisible)){
35304             Roo.dd.Registry.register(this.elNode, {
35305                 node: this.node,
35306                 handles: this.getDDHandles(),
35307                 isHandle: false
35308             });
35309         }
35310     },
35311
35312     getDDHandles : function(){
35313         return [this.iconNode, this.textNode];
35314     },
35315
35316     hide : function(){
35317         if(this.rendered){
35318             this.wrap.style.display = "none";
35319         }
35320     },
35321
35322     show : function(){
35323         if(this.rendered){
35324             this.wrap.style.display = "";
35325         }
35326     },
35327
35328     onContextMenu : function(e){
35329         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35330             e.preventDefault();
35331             this.focus();
35332             this.fireEvent("contextmenu", this.node, e);
35333         }
35334     },
35335
35336     onClick : function(e){
35337         if(this.dropping){
35338             e.stopEvent();
35339             return;
35340         }
35341         if(this.fireEvent("beforeclick", this.node, e) !== false){
35342             if(!this.disabled && this.node.attributes.href){
35343                 this.fireEvent("click", this.node, e);
35344                 return;
35345             }
35346             e.preventDefault();
35347             if(this.disabled){
35348                 return;
35349             }
35350
35351             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35352                 this.node.toggle();
35353             }
35354
35355             this.fireEvent("click", this.node, e);
35356         }else{
35357             e.stopEvent();
35358         }
35359     },
35360
35361     onDblClick : function(e){
35362         e.preventDefault();
35363         if(this.disabled){
35364             return;
35365         }
35366         if(this.checkbox){
35367             this.toggleCheck();
35368         }
35369         if(!this.animating && this.node.hasChildNodes()){
35370             this.node.toggle();
35371         }
35372         this.fireEvent("dblclick", this.node, e);
35373     },
35374
35375     onCheckChange : function(){
35376         var checked = this.checkbox.checked;
35377         this.node.attributes.checked = checked;
35378         this.fireEvent('checkchange', this.node, checked);
35379     },
35380
35381     ecClick : function(e){
35382         if(!this.animating && this.node.hasChildNodes()){
35383             this.node.toggle();
35384         }
35385     },
35386
35387     startDrop : function(){
35388         this.dropping = true;
35389     },
35390
35391     // delayed drop so the click event doesn't get fired on a drop
35392     endDrop : function(){
35393        setTimeout(function(){
35394            this.dropping = false;
35395        }.createDelegate(this), 50);
35396     },
35397
35398     expand : function(){
35399         this.updateExpandIcon();
35400         this.ctNode.style.display = "";
35401     },
35402
35403     focus : function(){
35404         if(!this.node.preventHScroll){
35405             try{this.anchor.focus();
35406             }catch(e){}
35407         }else if(!Roo.isIE){
35408             try{
35409                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35410                 var l = noscroll.scrollLeft;
35411                 this.anchor.focus();
35412                 noscroll.scrollLeft = l;
35413             }catch(e){}
35414         }
35415     },
35416
35417     toggleCheck : function(value){
35418         var cb = this.checkbox;
35419         if(cb){
35420             cb.checked = (value === undefined ? !cb.checked : value);
35421         }
35422     },
35423
35424     blur : function(){
35425         try{
35426             this.anchor.blur();
35427         }catch(e){}
35428     },
35429
35430     animExpand : function(callback){
35431         var ct = Roo.get(this.ctNode);
35432         ct.stopFx();
35433         if(!this.node.hasChildNodes()){
35434             this.updateExpandIcon();
35435             this.ctNode.style.display = "";
35436             Roo.callback(callback);
35437             return;
35438         }
35439         this.animating = true;
35440         this.updateExpandIcon();
35441
35442         ct.slideIn('t', {
35443            callback : function(){
35444                this.animating = false;
35445                Roo.callback(callback);
35446             },
35447             scope: this,
35448             duration: this.node.ownerTree.duration || .25
35449         });
35450     },
35451
35452     highlight : function(){
35453         var tree = this.node.getOwnerTree();
35454         Roo.fly(this.wrap).highlight(
35455             tree.hlColor || "C3DAF9",
35456             {endColor: tree.hlBaseColor}
35457         );
35458     },
35459
35460     collapse : function(){
35461         this.updateExpandIcon();
35462         this.ctNode.style.display = "none";
35463     },
35464
35465     animCollapse : function(callback){
35466         var ct = Roo.get(this.ctNode);
35467         ct.enableDisplayMode('block');
35468         ct.stopFx();
35469
35470         this.animating = true;
35471         this.updateExpandIcon();
35472
35473         ct.slideOut('t', {
35474             callback : function(){
35475                this.animating = false;
35476                Roo.callback(callback);
35477             },
35478             scope: this,
35479             duration: this.node.ownerTree.duration || .25
35480         });
35481     },
35482
35483     getContainer : function(){
35484         return this.ctNode;
35485     },
35486
35487     getEl : function(){
35488         return this.wrap;
35489     },
35490
35491     appendDDGhost : function(ghostNode){
35492         ghostNode.appendChild(this.elNode.cloneNode(true));
35493     },
35494
35495     getDDRepairXY : function(){
35496         return Roo.lib.Dom.getXY(this.iconNode);
35497     },
35498
35499     onRender : function(){
35500         this.render();
35501     },
35502
35503     render : function(bulkRender){
35504         var n = this.node, a = n.attributes;
35505         var targetNode = n.parentNode ?
35506               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35507
35508         if(!this.rendered){
35509             this.rendered = true;
35510
35511             this.renderElements(n, a, targetNode, bulkRender);
35512
35513             if(a.qtip){
35514                if(this.textNode.setAttributeNS){
35515                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35516                    if(a.qtipTitle){
35517                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35518                    }
35519                }else{
35520                    this.textNode.setAttribute("ext:qtip", a.qtip);
35521                    if(a.qtipTitle){
35522                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35523                    }
35524                }
35525             }else if(a.qtipCfg){
35526                 a.qtipCfg.target = Roo.id(this.textNode);
35527                 Roo.QuickTips.register(a.qtipCfg);
35528             }
35529             this.initEvents();
35530             if(!this.node.expanded){
35531                 this.updateExpandIcon();
35532             }
35533         }else{
35534             if(bulkRender === true) {
35535                 targetNode.appendChild(this.wrap);
35536             }
35537         }
35538     },
35539
35540     renderElements : function(n, a, targetNode, bulkRender)
35541     {
35542         // add some indent caching, this helps performance when rendering a large tree
35543         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35544         var t = n.getOwnerTree();
35545         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35546         if (typeof(n.attributes.html) != 'undefined') {
35547             txt = n.attributes.html;
35548         }
35549         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35550         var cb = typeof a.checked == 'boolean';
35551         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35552         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35553             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35554             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35555             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35556             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35557             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35558              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35559                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35560             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35561             "</li>"];
35562
35563         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35564             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35565                                 n.nextSibling.ui.getEl(), buf.join(""));
35566         }else{
35567             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35568         }
35569
35570         this.elNode = this.wrap.childNodes[0];
35571         this.ctNode = this.wrap.childNodes[1];
35572         var cs = this.elNode.childNodes;
35573         this.indentNode = cs[0];
35574         this.ecNode = cs[1];
35575         this.iconNode = cs[2];
35576         var index = 3;
35577         if(cb){
35578             this.checkbox = cs[3];
35579             index++;
35580         }
35581         this.anchor = cs[index];
35582         this.textNode = cs[index].firstChild;
35583     },
35584
35585     getAnchor : function(){
35586         return this.anchor;
35587     },
35588
35589     getTextEl : function(){
35590         return this.textNode;
35591     },
35592
35593     getIconEl : function(){
35594         return this.iconNode;
35595     },
35596
35597     isChecked : function(){
35598         return this.checkbox ? this.checkbox.checked : false;
35599     },
35600
35601     updateExpandIcon : function(){
35602         if(this.rendered){
35603             var n = this.node, c1, c2;
35604             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35605             var hasChild = n.hasChildNodes();
35606             if(hasChild){
35607                 if(n.expanded){
35608                     cls += "-minus";
35609                     c1 = "x-tree-node-collapsed";
35610                     c2 = "x-tree-node-expanded";
35611                 }else{
35612                     cls += "-plus";
35613                     c1 = "x-tree-node-expanded";
35614                     c2 = "x-tree-node-collapsed";
35615                 }
35616                 if(this.wasLeaf){
35617                     this.removeClass("x-tree-node-leaf");
35618                     this.wasLeaf = false;
35619                 }
35620                 if(this.c1 != c1 || this.c2 != c2){
35621                     Roo.fly(this.elNode).replaceClass(c1, c2);
35622                     this.c1 = c1; this.c2 = c2;
35623                 }
35624             }else{
35625                 // this changes non-leafs into leafs if they have no children.
35626                 // it's not very rational behaviour..
35627                 
35628                 if(!this.wasLeaf && this.node.leaf){
35629                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35630                     delete this.c1;
35631                     delete this.c2;
35632                     this.wasLeaf = true;
35633                 }
35634             }
35635             var ecc = "x-tree-ec-icon "+cls;
35636             if(this.ecc != ecc){
35637                 this.ecNode.className = ecc;
35638                 this.ecc = ecc;
35639             }
35640         }
35641     },
35642
35643     getChildIndent : function(){
35644         if(!this.childIndent){
35645             var buf = [];
35646             var p = this.node;
35647             while(p){
35648                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35649                     if(!p.isLast()) {
35650                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35651                     } else {
35652                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35653                     }
35654                 }
35655                 p = p.parentNode;
35656             }
35657             this.childIndent = buf.join("");
35658         }
35659         return this.childIndent;
35660     },
35661
35662     renderIndent : function(){
35663         if(this.rendered){
35664             var indent = "";
35665             var p = this.node.parentNode;
35666             if(p){
35667                 indent = p.ui.getChildIndent();
35668             }
35669             if(this.indentMarkup != indent){ // don't rerender if not required
35670                 this.indentNode.innerHTML = indent;
35671                 this.indentMarkup = indent;
35672             }
35673             this.updateExpandIcon();
35674         }
35675     }
35676 };
35677
35678 Roo.tree.RootTreeNodeUI = function(){
35679     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35680 };
35681 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35682     render : function(){
35683         if(!this.rendered){
35684             var targetNode = this.node.ownerTree.innerCt.dom;
35685             this.node.expanded = true;
35686             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35687             this.wrap = this.ctNode = targetNode.firstChild;
35688         }
35689     },
35690     collapse : function(){
35691     },
35692     expand : function(){
35693     }
35694 });/*
35695  * Based on:
35696  * Ext JS Library 1.1.1
35697  * Copyright(c) 2006-2007, Ext JS, LLC.
35698  *
35699  * Originally Released Under LGPL - original licence link has changed is not relivant.
35700  *
35701  * Fork - LGPL
35702  * <script type="text/javascript">
35703  */
35704 /**
35705  * @class Roo.tree.TreeLoader
35706  * @extends Roo.util.Observable
35707  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35708  * nodes from a specified URL. The response must be a javascript Array definition
35709  * who's elements are node definition objects. eg:
35710  * <pre><code>
35711 {  success : true,
35712    data :      [
35713    
35714     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35715     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35716     ]
35717 }
35718
35719
35720 </code></pre>
35721  * <br><br>
35722  * The old style respose with just an array is still supported, but not recommended.
35723  * <br><br>
35724  *
35725  * A server request is sent, and child nodes are loaded only when a node is expanded.
35726  * The loading node's id is passed to the server under the parameter name "node" to
35727  * enable the server to produce the correct child nodes.
35728  * <br><br>
35729  * To pass extra parameters, an event handler may be attached to the "beforeload"
35730  * event, and the parameters specified in the TreeLoader's baseParams property:
35731  * <pre><code>
35732     myTreeLoader.on("beforeload", function(treeLoader, node) {
35733         this.baseParams.category = node.attributes.category;
35734     }, this);
35735 </code></pre><
35736  * This would pass an HTTP parameter called "category" to the server containing
35737  * the value of the Node's "category" attribute.
35738  * @constructor
35739  * Creates a new Treeloader.
35740  * @param {Object} config A config object containing config properties.
35741  */
35742 Roo.tree.TreeLoader = function(config){
35743     this.baseParams = {};
35744     this.requestMethod = "POST";
35745     Roo.apply(this, config);
35746
35747     this.addEvents({
35748     
35749         /**
35750          * @event beforeload
35751          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35752          * @param {Object} This TreeLoader object.
35753          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35754          * @param {Object} callback The callback function specified in the {@link #load} call.
35755          */
35756         beforeload : true,
35757         /**
35758          * @event load
35759          * Fires when the node has been successfuly loaded.
35760          * @param {Object} This TreeLoader object.
35761          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35762          * @param {Object} response The response object containing the data from the server.
35763          */
35764         load : true,
35765         /**
35766          * @event loadexception
35767          * Fires if the network request failed.
35768          * @param {Object} This TreeLoader object.
35769          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35770          * @param {Object} response The response object containing the data from the server.
35771          */
35772         loadexception : true,
35773         /**
35774          * @event create
35775          * Fires before a node is created, enabling you to return custom Node types 
35776          * @param {Object} This TreeLoader object.
35777          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35778          */
35779         create : true
35780     });
35781
35782     Roo.tree.TreeLoader.superclass.constructor.call(this);
35783 };
35784
35785 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35786     /**
35787     * @cfg {String} dataUrl The URL from which to request a Json string which
35788     * specifies an array of node definition object representing the child nodes
35789     * to be loaded.
35790     */
35791     /**
35792     * @cfg {String} requestMethod either GET or POST
35793     * defaults to POST (due to BC)
35794     * to be loaded.
35795     */
35796     /**
35797     * @cfg {Object} baseParams (optional) An object containing properties which
35798     * specify HTTP parameters to be passed to each request for child nodes.
35799     */
35800     /**
35801     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35802     * created by this loader. If the attributes sent by the server have an attribute in this object,
35803     * they take priority.
35804     */
35805     /**
35806     * @cfg {Object} uiProviders (optional) An object containing properties which
35807     * 
35808     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35809     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35810     * <i>uiProvider</i> attribute of a returned child node is a string rather
35811     * than a reference to a TreeNodeUI implementation, this that string value
35812     * is used as a property name in the uiProviders object. You can define the provider named
35813     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35814     */
35815     uiProviders : {},
35816
35817     /**
35818     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35819     * child nodes before loading.
35820     */
35821     clearOnLoad : true,
35822
35823     /**
35824     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35825     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35826     * Grid query { data : [ .....] }
35827     */
35828     
35829     root : false,
35830      /**
35831     * @cfg {String} queryParam (optional) 
35832     * Name of the query as it will be passed on the querystring (defaults to 'node')
35833     * eg. the request will be ?node=[id]
35834     */
35835     
35836     
35837     queryParam: false,
35838     
35839     /**
35840      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35841      * This is called automatically when a node is expanded, but may be used to reload
35842      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35843      * @param {Roo.tree.TreeNode} node
35844      * @param {Function} callback
35845      */
35846     load : function(node, callback){
35847         if(this.clearOnLoad){
35848             while(node.firstChild){
35849                 node.removeChild(node.firstChild);
35850             }
35851         }
35852         if(node.attributes.children){ // preloaded json children
35853             var cs = node.attributes.children;
35854             for(var i = 0, len = cs.length; i < len; i++){
35855                 node.appendChild(this.createNode(cs[i]));
35856             }
35857             if(typeof callback == "function"){
35858                 callback();
35859             }
35860         }else if(this.dataUrl){
35861             this.requestData(node, callback);
35862         }
35863     },
35864
35865     getParams: function(node){
35866         var buf = [], bp = this.baseParams;
35867         for(var key in bp){
35868             if(typeof bp[key] != "function"){
35869                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
35870             }
35871         }
35872         var n = this.queryParam === false ? 'node' : this.queryParam;
35873         buf.push(n + "=", encodeURIComponent(node.id));
35874         return buf.join("");
35875     },
35876
35877     requestData : function(node, callback){
35878         if(this.fireEvent("beforeload", this, node, callback) !== false){
35879             this.transId = Roo.Ajax.request({
35880                 method:this.requestMethod,
35881                 url: this.dataUrl||this.url,
35882                 success: this.handleResponse,
35883                 failure: this.handleFailure,
35884                 scope: this,
35885                 argument: {callback: callback, node: node},
35886                 params: this.getParams(node)
35887             });
35888         }else{
35889             // if the load is cancelled, make sure we notify
35890             // the node that we are done
35891             if(typeof callback == "function"){
35892                 callback();
35893             }
35894         }
35895     },
35896
35897     isLoading : function(){
35898         return this.transId ? true : false;
35899     },
35900
35901     abort : function(){
35902         if(this.isLoading()){
35903             Roo.Ajax.abort(this.transId);
35904         }
35905     },
35906
35907     // private
35908     createNode : function(attr)
35909     {
35910         // apply baseAttrs, nice idea Corey!
35911         if(this.baseAttrs){
35912             Roo.applyIf(attr, this.baseAttrs);
35913         }
35914         if(this.applyLoader !== false){
35915             attr.loader = this;
35916         }
35917         // uiProvider = depreciated..
35918         
35919         if(typeof(attr.uiProvider) == 'string'){
35920            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
35921                 /**  eval:var:attr */ eval(attr.uiProvider);
35922         }
35923         if(typeof(this.uiProviders['default']) != 'undefined') {
35924             attr.uiProvider = this.uiProviders['default'];
35925         }
35926         
35927         this.fireEvent('create', this, attr);
35928         
35929         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
35930         return(attr.leaf ?
35931                         new Roo.tree.TreeNode(attr) :
35932                         new Roo.tree.AsyncTreeNode(attr));
35933     },
35934
35935     processResponse : function(response, node, callback)
35936     {
35937         var json = response.responseText;
35938         try {
35939             
35940             var o = Roo.decode(json);
35941             
35942             if (this.root === false && typeof(o.success) != undefined) {
35943                 this.root = 'data'; // the default behaviour for list like data..
35944                 }
35945                 
35946             if (this.root !== false &&  !o.success) {
35947                 // it's a failure condition.
35948                 var a = response.argument;
35949                 this.fireEvent("loadexception", this, a.node, response);
35950                 Roo.log("Load failed - should have a handler really");
35951                 return;
35952             }
35953             
35954             
35955             
35956             if (this.root !== false) {
35957                  o = o[this.root];
35958             }
35959             
35960             for(var i = 0, len = o.length; i < len; i++){
35961                 var n = this.createNode(o[i]);
35962                 if(n){
35963                     node.appendChild(n);
35964                 }
35965             }
35966             if(typeof callback == "function"){
35967                 callback(this, node);
35968             }
35969         }catch(e){
35970             this.handleFailure(response);
35971         }
35972     },
35973
35974     handleResponse : function(response){
35975         this.transId = false;
35976         var a = response.argument;
35977         this.processResponse(response, a.node, a.callback);
35978         this.fireEvent("load", this, a.node, response);
35979     },
35980
35981     handleFailure : function(response)
35982     {
35983         // should handle failure better..
35984         this.transId = false;
35985         var a = response.argument;
35986         this.fireEvent("loadexception", this, a.node, response);
35987         if(typeof a.callback == "function"){
35988             a.callback(this, a.node);
35989         }
35990     }
35991 });/*
35992  * Based on:
35993  * Ext JS Library 1.1.1
35994  * Copyright(c) 2006-2007, Ext JS, LLC.
35995  *
35996  * Originally Released Under LGPL - original licence link has changed is not relivant.
35997  *
35998  * Fork - LGPL
35999  * <script type="text/javascript">
36000  */
36001
36002 /**
36003 * @class Roo.tree.TreeFilter
36004 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36005 * @param {TreePanel} tree
36006 * @param {Object} config (optional)
36007  */
36008 Roo.tree.TreeFilter = function(tree, config){
36009     this.tree = tree;
36010     this.filtered = {};
36011     Roo.apply(this, config);
36012 };
36013
36014 Roo.tree.TreeFilter.prototype = {
36015     clearBlank:false,
36016     reverse:false,
36017     autoClear:false,
36018     remove:false,
36019
36020      /**
36021      * Filter the data by a specific attribute.
36022      * @param {String/RegExp} value Either string that the attribute value
36023      * should start with or a RegExp to test against the attribute
36024      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36025      * @param {TreeNode} startNode (optional) The node to start the filter at.
36026      */
36027     filter : function(value, attr, startNode){
36028         attr = attr || "text";
36029         var f;
36030         if(typeof value == "string"){
36031             var vlen = value.length;
36032             // auto clear empty filter
36033             if(vlen == 0 && this.clearBlank){
36034                 this.clear();
36035                 return;
36036             }
36037             value = value.toLowerCase();
36038             f = function(n){
36039                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36040             };
36041         }else if(value.exec){ // regex?
36042             f = function(n){
36043                 return value.test(n.attributes[attr]);
36044             };
36045         }else{
36046             throw 'Illegal filter type, must be string or regex';
36047         }
36048         this.filterBy(f, null, startNode);
36049         },
36050
36051     /**
36052      * Filter by a function. The passed function will be called with each
36053      * node in the tree (or from the startNode). If the function returns true, the node is kept
36054      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36055      * @param {Function} fn The filter function
36056      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36057      */
36058     filterBy : function(fn, scope, startNode){
36059         startNode = startNode || this.tree.root;
36060         if(this.autoClear){
36061             this.clear();
36062         }
36063         var af = this.filtered, rv = this.reverse;
36064         var f = function(n){
36065             if(n == startNode){
36066                 return true;
36067             }
36068             if(af[n.id]){
36069                 return false;
36070             }
36071             var m = fn.call(scope || n, n);
36072             if(!m || rv){
36073                 af[n.id] = n;
36074                 n.ui.hide();
36075                 return false;
36076             }
36077             return true;
36078         };
36079         startNode.cascade(f);
36080         if(this.remove){
36081            for(var id in af){
36082                if(typeof id != "function"){
36083                    var n = af[id];
36084                    if(n && n.parentNode){
36085                        n.parentNode.removeChild(n);
36086                    }
36087                }
36088            }
36089         }
36090     },
36091
36092     /**
36093      * Clears the current filter. Note: with the "remove" option
36094      * set a filter cannot be cleared.
36095      */
36096     clear : function(){
36097         var t = this.tree;
36098         var af = this.filtered;
36099         for(var id in af){
36100             if(typeof id != "function"){
36101                 var n = af[id];
36102                 if(n){
36103                     n.ui.show();
36104                 }
36105             }
36106         }
36107         this.filtered = {};
36108     }
36109 };
36110 /*
36111  * Based on:
36112  * Ext JS Library 1.1.1
36113  * Copyright(c) 2006-2007, Ext JS, LLC.
36114  *
36115  * Originally Released Under LGPL - original licence link has changed is not relivant.
36116  *
36117  * Fork - LGPL
36118  * <script type="text/javascript">
36119  */
36120  
36121
36122 /**
36123  * @class Roo.tree.TreeSorter
36124  * Provides sorting of nodes in a TreePanel
36125  * 
36126  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36127  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36128  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36129  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36130  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36131  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36132  * @constructor
36133  * @param {TreePanel} tree
36134  * @param {Object} config
36135  */
36136 Roo.tree.TreeSorter = function(tree, config){
36137     Roo.apply(this, config);
36138     tree.on("beforechildrenrendered", this.doSort, this);
36139     tree.on("append", this.updateSort, this);
36140     tree.on("insert", this.updateSort, this);
36141     
36142     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36143     var p = this.property || "text";
36144     var sortType = this.sortType;
36145     var fs = this.folderSort;
36146     var cs = this.caseSensitive === true;
36147     var leafAttr = this.leafAttr || 'leaf';
36148
36149     this.sortFn = function(n1, n2){
36150         if(fs){
36151             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36152                 return 1;
36153             }
36154             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36155                 return -1;
36156             }
36157         }
36158         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36159         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36160         if(v1 < v2){
36161                         return dsc ? +1 : -1;
36162                 }else if(v1 > v2){
36163                         return dsc ? -1 : +1;
36164         }else{
36165                 return 0;
36166         }
36167     };
36168 };
36169
36170 Roo.tree.TreeSorter.prototype = {
36171     doSort : function(node){
36172         node.sort(this.sortFn);
36173     },
36174     
36175     compareNodes : function(n1, n2){
36176         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36177     },
36178     
36179     updateSort : function(tree, node){
36180         if(node.childrenRendered){
36181             this.doSort.defer(1, this, [node]);
36182         }
36183     }
36184 };/*
36185  * Based on:
36186  * Ext JS Library 1.1.1
36187  * Copyright(c) 2006-2007, Ext JS, LLC.
36188  *
36189  * Originally Released Under LGPL - original licence link has changed is not relivant.
36190  *
36191  * Fork - LGPL
36192  * <script type="text/javascript">
36193  */
36194
36195 if(Roo.dd.DropZone){
36196     
36197 Roo.tree.TreeDropZone = function(tree, config){
36198     this.allowParentInsert = false;
36199     this.allowContainerDrop = false;
36200     this.appendOnly = false;
36201     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36202     this.tree = tree;
36203     this.lastInsertClass = "x-tree-no-status";
36204     this.dragOverData = {};
36205 };
36206
36207 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36208     ddGroup : "TreeDD",
36209     scroll:  true,
36210     
36211     expandDelay : 1000,
36212     
36213     expandNode : function(node){
36214         if(node.hasChildNodes() && !node.isExpanded()){
36215             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36216         }
36217     },
36218     
36219     queueExpand : function(node){
36220         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36221     },
36222     
36223     cancelExpand : function(){
36224         if(this.expandProcId){
36225             clearTimeout(this.expandProcId);
36226             this.expandProcId = false;
36227         }
36228     },
36229     
36230     isValidDropPoint : function(n, pt, dd, e, data){
36231         if(!n || !data){ return false; }
36232         var targetNode = n.node;
36233         var dropNode = data.node;
36234         // default drop rules
36235         if(!(targetNode && targetNode.isTarget && pt)){
36236             return false;
36237         }
36238         if(pt == "append" && targetNode.allowChildren === false){
36239             return false;
36240         }
36241         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36242             return false;
36243         }
36244         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36245             return false;
36246         }
36247         // reuse the object
36248         var overEvent = this.dragOverData;
36249         overEvent.tree = this.tree;
36250         overEvent.target = targetNode;
36251         overEvent.data = data;
36252         overEvent.point = pt;
36253         overEvent.source = dd;
36254         overEvent.rawEvent = e;
36255         overEvent.dropNode = dropNode;
36256         overEvent.cancel = false;  
36257         var result = this.tree.fireEvent("nodedragover", overEvent);
36258         return overEvent.cancel === false && result !== false;
36259     },
36260     
36261     getDropPoint : function(e, n, dd)
36262     {
36263         var tn = n.node;
36264         if(tn.isRoot){
36265             return tn.allowChildren !== false ? "append" : false; // always append for root
36266         }
36267         var dragEl = n.ddel;
36268         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36269         var y = Roo.lib.Event.getPageY(e);
36270         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36271         
36272         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36273         var noAppend = tn.allowChildren === false;
36274         if(this.appendOnly || tn.parentNode.allowChildren === false){
36275             return noAppend ? false : "append";
36276         }
36277         var noBelow = false;
36278         if(!this.allowParentInsert){
36279             noBelow = tn.hasChildNodes() && tn.isExpanded();
36280         }
36281         var q = (b - t) / (noAppend ? 2 : 3);
36282         if(y >= t && y < (t + q)){
36283             return "above";
36284         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36285             return "below";
36286         }else{
36287             return "append";
36288         }
36289     },
36290     
36291     onNodeEnter : function(n, dd, e, data)
36292     {
36293         this.cancelExpand();
36294     },
36295     
36296     onNodeOver : function(n, dd, e, data)
36297     {
36298        
36299         var pt = this.getDropPoint(e, n, dd);
36300         var node = n.node;
36301         
36302         // auto node expand check
36303         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36304             this.queueExpand(node);
36305         }else if(pt != "append"){
36306             this.cancelExpand();
36307         }
36308         
36309         // set the insert point style on the target node
36310         var returnCls = this.dropNotAllowed;
36311         if(this.isValidDropPoint(n, pt, dd, e, data)){
36312            if(pt){
36313                var el = n.ddel;
36314                var cls;
36315                if(pt == "above"){
36316                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36317                    cls = "x-tree-drag-insert-above";
36318                }else if(pt == "below"){
36319                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36320                    cls = "x-tree-drag-insert-below";
36321                }else{
36322                    returnCls = "x-tree-drop-ok-append";
36323                    cls = "x-tree-drag-append";
36324                }
36325                if(this.lastInsertClass != cls){
36326                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36327                    this.lastInsertClass = cls;
36328                }
36329            }
36330        }
36331        return returnCls;
36332     },
36333     
36334     onNodeOut : function(n, dd, e, data){
36335         
36336         this.cancelExpand();
36337         this.removeDropIndicators(n);
36338     },
36339     
36340     onNodeDrop : function(n, dd, e, data){
36341         var point = this.getDropPoint(e, n, dd);
36342         var targetNode = n.node;
36343         targetNode.ui.startDrop();
36344         if(!this.isValidDropPoint(n, point, dd, e, data)){
36345             targetNode.ui.endDrop();
36346             return false;
36347         }
36348         // first try to find the drop node
36349         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36350         var dropEvent = {
36351             tree : this.tree,
36352             target: targetNode,
36353             data: data,
36354             point: point,
36355             source: dd,
36356             rawEvent: e,
36357             dropNode: dropNode,
36358             cancel: !dropNode   
36359         };
36360         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36361         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36362             targetNode.ui.endDrop();
36363             return false;
36364         }
36365         // allow target changing
36366         targetNode = dropEvent.target;
36367         if(point == "append" && !targetNode.isExpanded()){
36368             targetNode.expand(false, null, function(){
36369                 this.completeDrop(dropEvent);
36370             }.createDelegate(this));
36371         }else{
36372             this.completeDrop(dropEvent);
36373         }
36374         return true;
36375     },
36376     
36377     completeDrop : function(de){
36378         var ns = de.dropNode, p = de.point, t = de.target;
36379         if(!(ns instanceof Array)){
36380             ns = [ns];
36381         }
36382         var n;
36383         for(var i = 0, len = ns.length; i < len; i++){
36384             n = ns[i];
36385             if(p == "above"){
36386                 t.parentNode.insertBefore(n, t);
36387             }else if(p == "below"){
36388                 t.parentNode.insertBefore(n, t.nextSibling);
36389             }else{
36390                 t.appendChild(n);
36391             }
36392         }
36393         n.ui.focus();
36394         if(this.tree.hlDrop){
36395             n.ui.highlight();
36396         }
36397         t.ui.endDrop();
36398         this.tree.fireEvent("nodedrop", de);
36399     },
36400     
36401     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36402         if(this.tree.hlDrop){
36403             dropNode.ui.focus();
36404             dropNode.ui.highlight();
36405         }
36406         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36407     },
36408     
36409     getTree : function(){
36410         return this.tree;
36411     },
36412     
36413     removeDropIndicators : function(n){
36414         if(n && n.ddel){
36415             var el = n.ddel;
36416             Roo.fly(el).removeClass([
36417                     "x-tree-drag-insert-above",
36418                     "x-tree-drag-insert-below",
36419                     "x-tree-drag-append"]);
36420             this.lastInsertClass = "_noclass";
36421         }
36422     },
36423     
36424     beforeDragDrop : function(target, e, id){
36425         this.cancelExpand();
36426         return true;
36427     },
36428     
36429     afterRepair : function(data){
36430         if(data && Roo.enableFx){
36431             data.node.ui.highlight();
36432         }
36433         this.hideProxy();
36434     } 
36435     
36436 });
36437
36438 }
36439 /*
36440  * Based on:
36441  * Ext JS Library 1.1.1
36442  * Copyright(c) 2006-2007, Ext JS, LLC.
36443  *
36444  * Originally Released Under LGPL - original licence link has changed is not relivant.
36445  *
36446  * Fork - LGPL
36447  * <script type="text/javascript">
36448  */
36449  
36450
36451 if(Roo.dd.DragZone){
36452 Roo.tree.TreeDragZone = function(tree, config){
36453     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36454     this.tree = tree;
36455 };
36456
36457 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36458     ddGroup : "TreeDD",
36459    
36460     onBeforeDrag : function(data, e){
36461         var n = data.node;
36462         return n && n.draggable && !n.disabled;
36463     },
36464      
36465     
36466     onInitDrag : function(e){
36467         var data = this.dragData;
36468         this.tree.getSelectionModel().select(data.node);
36469         this.proxy.update("");
36470         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36471         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36472     },
36473     
36474     getRepairXY : function(e, data){
36475         return data.node.ui.getDDRepairXY();
36476     },
36477     
36478     onEndDrag : function(data, e){
36479         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36480         
36481         
36482     },
36483     
36484     onValidDrop : function(dd, e, id){
36485         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36486         this.hideProxy();
36487     },
36488     
36489     beforeInvalidDrop : function(e, id){
36490         // this scrolls the original position back into view
36491         var sm = this.tree.getSelectionModel();
36492         sm.clearSelections();
36493         sm.select(this.dragData.node);
36494     }
36495 });
36496 }/*
36497  * Based on:
36498  * Ext JS Library 1.1.1
36499  * Copyright(c) 2006-2007, Ext JS, LLC.
36500  *
36501  * Originally Released Under LGPL - original licence link has changed is not relivant.
36502  *
36503  * Fork - LGPL
36504  * <script type="text/javascript">
36505  */
36506 /**
36507  * @class Roo.tree.TreeEditor
36508  * @extends Roo.Editor
36509  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36510  * as the editor field.
36511  * @constructor
36512  * @param {Object} config (used to be the tree panel.)
36513  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36514  * 
36515  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36516  * @cfg {Roo.form.TextField|Object} field The field configuration
36517  *
36518  * 
36519  */
36520 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36521     var tree = config;
36522     var field;
36523     if (oldconfig) { // old style..
36524         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36525     } else {
36526         // new style..
36527         tree = config.tree;
36528         config.field = config.field  || {};
36529         config.field.xtype = 'TextField';
36530         field = Roo.factory(config.field, Roo.form);
36531     }
36532     config = config || {};
36533     
36534     
36535     this.addEvents({
36536         /**
36537          * @event beforenodeedit
36538          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36539          * false from the handler of this event.
36540          * @param {Editor} this
36541          * @param {Roo.tree.Node} node 
36542          */
36543         "beforenodeedit" : true
36544     });
36545     
36546     //Roo.log(config);
36547     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36548
36549     this.tree = tree;
36550
36551     tree.on('beforeclick', this.beforeNodeClick, this);
36552     tree.getTreeEl().on('mousedown', this.hide, this);
36553     this.on('complete', this.updateNode, this);
36554     this.on('beforestartedit', this.fitToTree, this);
36555     this.on('startedit', this.bindScroll, this, {delay:10});
36556     this.on('specialkey', this.onSpecialKey, this);
36557 };
36558
36559 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36560     /**
36561      * @cfg {String} alignment
36562      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36563      */
36564     alignment: "l-l",
36565     // inherit
36566     autoSize: false,
36567     /**
36568      * @cfg {Boolean} hideEl
36569      * True to hide the bound element while the editor is displayed (defaults to false)
36570      */
36571     hideEl : false,
36572     /**
36573      * @cfg {String} cls
36574      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36575      */
36576     cls: "x-small-editor x-tree-editor",
36577     /**
36578      * @cfg {Boolean} shim
36579      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36580      */
36581     shim:false,
36582     // inherit
36583     shadow:"frame",
36584     /**
36585      * @cfg {Number} maxWidth
36586      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36587      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36588      * scroll and client offsets into account prior to each edit.
36589      */
36590     maxWidth: 250,
36591
36592     editDelay : 350,
36593
36594     // private
36595     fitToTree : function(ed, el){
36596         var td = this.tree.getTreeEl().dom, nd = el.dom;
36597         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36598             td.scrollLeft = nd.offsetLeft;
36599         }
36600         var w = Math.min(
36601                 this.maxWidth,
36602                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36603         this.setSize(w, '');
36604         
36605         return this.fireEvent('beforenodeedit', this, this.editNode);
36606         
36607     },
36608
36609     // private
36610     triggerEdit : function(node){
36611         this.completeEdit();
36612         this.editNode = node;
36613         this.startEdit(node.ui.textNode, node.text);
36614     },
36615
36616     // private
36617     bindScroll : function(){
36618         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36619     },
36620
36621     // private
36622     beforeNodeClick : function(node, e){
36623         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36624         this.lastClick = new Date();
36625         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36626             e.stopEvent();
36627             this.triggerEdit(node);
36628             return false;
36629         }
36630         return true;
36631     },
36632
36633     // private
36634     updateNode : function(ed, value){
36635         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36636         this.editNode.setText(value);
36637     },
36638
36639     // private
36640     onHide : function(){
36641         Roo.tree.TreeEditor.superclass.onHide.call(this);
36642         if(this.editNode){
36643             this.editNode.ui.focus();
36644         }
36645     },
36646
36647     // private
36648     onSpecialKey : function(field, e){
36649         var k = e.getKey();
36650         if(k == e.ESC){
36651             e.stopEvent();
36652             this.cancelEdit();
36653         }else if(k == e.ENTER && !e.hasModifier()){
36654             e.stopEvent();
36655             this.completeEdit();
36656         }
36657     }
36658 });//<Script type="text/javascript">
36659 /*
36660  * Based on:
36661  * Ext JS Library 1.1.1
36662  * Copyright(c) 2006-2007, Ext JS, LLC.
36663  *
36664  * Originally Released Under LGPL - original licence link has changed is not relivant.
36665  *
36666  * Fork - LGPL
36667  * <script type="text/javascript">
36668  */
36669  
36670 /**
36671  * Not documented??? - probably should be...
36672  */
36673
36674 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36675     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36676     
36677     renderElements : function(n, a, targetNode, bulkRender){
36678         //consel.log("renderElements?");
36679         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36680
36681         var t = n.getOwnerTree();
36682         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36683         
36684         var cols = t.columns;
36685         var bw = t.borderWidth;
36686         var c = cols[0];
36687         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36688          var cb = typeof a.checked == "boolean";
36689         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36690         var colcls = 'x-t-' + tid + '-c0';
36691         var buf = [
36692             '<li class="x-tree-node">',
36693             
36694                 
36695                 '<div class="x-tree-node-el ', a.cls,'">',
36696                     // extran...
36697                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36698                 
36699                 
36700                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36701                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36702                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36703                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36704                            (a.iconCls ? ' '+a.iconCls : ''),
36705                            '" unselectable="on" />',
36706                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36707                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36708                              
36709                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36710                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36711                             '<span unselectable="on" qtip="' + tx + '">',
36712                              tx,
36713                              '</span></a>' ,
36714                     '</div>',
36715                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36716                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36717                  ];
36718         for(var i = 1, len = cols.length; i < len; i++){
36719             c = cols[i];
36720             colcls = 'x-t-' + tid + '-c' +i;
36721             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36722             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36723                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36724                       "</div>");
36725          }
36726          
36727          buf.push(
36728             '</a>',
36729             '<div class="x-clear"></div></div>',
36730             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36731             "</li>");
36732         
36733         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36734             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36735                                 n.nextSibling.ui.getEl(), buf.join(""));
36736         }else{
36737             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36738         }
36739         var el = this.wrap.firstChild;
36740         this.elRow = el;
36741         this.elNode = el.firstChild;
36742         this.ranchor = el.childNodes[1];
36743         this.ctNode = this.wrap.childNodes[1];
36744         var cs = el.firstChild.childNodes;
36745         this.indentNode = cs[0];
36746         this.ecNode = cs[1];
36747         this.iconNode = cs[2];
36748         var index = 3;
36749         if(cb){
36750             this.checkbox = cs[3];
36751             index++;
36752         }
36753         this.anchor = cs[index];
36754         
36755         this.textNode = cs[index].firstChild;
36756         
36757         //el.on("click", this.onClick, this);
36758         //el.on("dblclick", this.onDblClick, this);
36759         
36760         
36761        // console.log(this);
36762     },
36763     initEvents : function(){
36764         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36765         
36766             
36767         var a = this.ranchor;
36768
36769         var el = Roo.get(a);
36770
36771         if(Roo.isOpera){ // opera render bug ignores the CSS
36772             el.setStyle("text-decoration", "none");
36773         }
36774
36775         el.on("click", this.onClick, this);
36776         el.on("dblclick", this.onDblClick, this);
36777         el.on("contextmenu", this.onContextMenu, this);
36778         
36779     },
36780     
36781     /*onSelectedChange : function(state){
36782         if(state){
36783             this.focus();
36784             this.addClass("x-tree-selected");
36785         }else{
36786             //this.blur();
36787             this.removeClass("x-tree-selected");
36788         }
36789     },*/
36790     addClass : function(cls){
36791         if(this.elRow){
36792             Roo.fly(this.elRow).addClass(cls);
36793         }
36794         
36795     },
36796     
36797     
36798     removeClass : function(cls){
36799         if(this.elRow){
36800             Roo.fly(this.elRow).removeClass(cls);
36801         }
36802     }
36803
36804     
36805     
36806 });//<Script type="text/javascript">
36807
36808 /*
36809  * Based on:
36810  * Ext JS Library 1.1.1
36811  * Copyright(c) 2006-2007, Ext JS, LLC.
36812  *
36813  * Originally Released Under LGPL - original licence link has changed is not relivant.
36814  *
36815  * Fork - LGPL
36816  * <script type="text/javascript">
36817  */
36818  
36819
36820 /**
36821  * @class Roo.tree.ColumnTree
36822  * @extends Roo.data.TreePanel
36823  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36824  * @cfg {int} borderWidth  compined right/left border allowance
36825  * @constructor
36826  * @param {String/HTMLElement/Element} el The container element
36827  * @param {Object} config
36828  */
36829 Roo.tree.ColumnTree =  function(el, config)
36830 {
36831    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36832    this.addEvents({
36833         /**
36834         * @event resize
36835         * Fire this event on a container when it resizes
36836         * @param {int} w Width
36837         * @param {int} h Height
36838         */
36839        "resize" : true
36840     });
36841     this.on('resize', this.onResize, this);
36842 };
36843
36844 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36845     //lines:false,
36846     
36847     
36848     borderWidth: Roo.isBorderBox ? 0 : 2, 
36849     headEls : false,
36850     
36851     render : function(){
36852         // add the header.....
36853        
36854         Roo.tree.ColumnTree.superclass.render.apply(this);
36855         
36856         this.el.addClass('x-column-tree');
36857         
36858         this.headers = this.el.createChild(
36859             {cls:'x-tree-headers'},this.innerCt.dom);
36860    
36861         var cols = this.columns, c;
36862         var totalWidth = 0;
36863         this.headEls = [];
36864         var  len = cols.length;
36865         for(var i = 0; i < len; i++){
36866              c = cols[i];
36867              totalWidth += c.width;
36868             this.headEls.push(this.headers.createChild({
36869                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
36870                  cn: {
36871                      cls:'x-tree-hd-text',
36872                      html: c.header
36873                  },
36874                  style:'width:'+(c.width-this.borderWidth)+'px;'
36875              }));
36876         }
36877         this.headers.createChild({cls:'x-clear'});
36878         // prevent floats from wrapping when clipped
36879         this.headers.setWidth(totalWidth);
36880         //this.innerCt.setWidth(totalWidth);
36881         this.innerCt.setStyle({ overflow: 'auto' });
36882         this.onResize(this.width, this.height);
36883              
36884         
36885     },
36886     onResize : function(w,h)
36887     {
36888         this.height = h;
36889         this.width = w;
36890         // resize cols..
36891         this.innerCt.setWidth(this.width);
36892         this.innerCt.setHeight(this.height-20);
36893         
36894         // headers...
36895         var cols = this.columns, c;
36896         var totalWidth = 0;
36897         var expEl = false;
36898         var len = cols.length;
36899         for(var i = 0; i < len; i++){
36900             c = cols[i];
36901             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
36902                 // it's the expander..
36903                 expEl  = this.headEls[i];
36904                 continue;
36905             }
36906             totalWidth += c.width;
36907             
36908         }
36909         if (expEl) {
36910             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
36911         }
36912         this.headers.setWidth(w-20);
36913
36914         
36915         
36916         
36917     }
36918 });
36919 /*
36920  * Based on:
36921  * Ext JS Library 1.1.1
36922  * Copyright(c) 2006-2007, Ext JS, LLC.
36923  *
36924  * Originally Released Under LGPL - original licence link has changed is not relivant.
36925  *
36926  * Fork - LGPL
36927  * <script type="text/javascript">
36928  */
36929  
36930 /**
36931  * @class Roo.menu.Menu
36932  * @extends Roo.util.Observable
36933  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
36934  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
36935  * @constructor
36936  * Creates a new Menu
36937  * @param {Object} config Configuration options
36938  */
36939 Roo.menu.Menu = function(config){
36940     Roo.apply(this, config);
36941     this.id = this.id || Roo.id();
36942     this.addEvents({
36943         /**
36944          * @event beforeshow
36945          * Fires before this menu is displayed
36946          * @param {Roo.menu.Menu} this
36947          */
36948         beforeshow : true,
36949         /**
36950          * @event beforehide
36951          * Fires before this menu is hidden
36952          * @param {Roo.menu.Menu} this
36953          */
36954         beforehide : true,
36955         /**
36956          * @event show
36957          * Fires after this menu is displayed
36958          * @param {Roo.menu.Menu} this
36959          */
36960         show : true,
36961         /**
36962          * @event hide
36963          * Fires after this menu is hidden
36964          * @param {Roo.menu.Menu} this
36965          */
36966         hide : true,
36967         /**
36968          * @event click
36969          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
36970          * @param {Roo.menu.Menu} this
36971          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36972          * @param {Roo.EventObject} e
36973          */
36974         click : true,
36975         /**
36976          * @event mouseover
36977          * Fires when the mouse is hovering over this menu
36978          * @param {Roo.menu.Menu} this
36979          * @param {Roo.EventObject} e
36980          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36981          */
36982         mouseover : true,
36983         /**
36984          * @event mouseout
36985          * Fires when the mouse exits this menu
36986          * @param {Roo.menu.Menu} this
36987          * @param {Roo.EventObject} e
36988          * @param {Roo.menu.Item} menuItem The menu item that was clicked
36989          */
36990         mouseout : true,
36991         /**
36992          * @event itemclick
36993          * Fires when a menu item contained in this menu is clicked
36994          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
36995          * @param {Roo.EventObject} e
36996          */
36997         itemclick: true
36998     });
36999     if (this.registerMenu) {
37000         Roo.menu.MenuMgr.register(this);
37001     }
37002     
37003     var mis = this.items;
37004     this.items = new Roo.util.MixedCollection();
37005     if(mis){
37006         this.add.apply(this, mis);
37007     }
37008 };
37009
37010 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37011     /**
37012      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37013      */
37014     minWidth : 120,
37015     /**
37016      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37017      * for bottom-right shadow (defaults to "sides")
37018      */
37019     shadow : "sides",
37020     /**
37021      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37022      * this menu (defaults to "tl-tr?")
37023      */
37024     subMenuAlign : "tl-tr?",
37025     /**
37026      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37027      * relative to its element of origin (defaults to "tl-bl?")
37028      */
37029     defaultAlign : "tl-bl?",
37030     /**
37031      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37032      */
37033     allowOtherMenus : false,
37034     /**
37035      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37036      */
37037     registerMenu : true,
37038
37039     hidden:true,
37040
37041     // private
37042     render : function(){
37043         if(this.el){
37044             return;
37045         }
37046         var el = this.el = new Roo.Layer({
37047             cls: "x-menu",
37048             shadow:this.shadow,
37049             constrain: false,
37050             parentEl: this.parentEl || document.body,
37051             zindex:15000
37052         });
37053
37054         this.keyNav = new Roo.menu.MenuNav(this);
37055
37056         if(this.plain){
37057             el.addClass("x-menu-plain");
37058         }
37059         if(this.cls){
37060             el.addClass(this.cls);
37061         }
37062         // generic focus element
37063         this.focusEl = el.createChild({
37064             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37065         });
37066         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37067         //disabling touch- as it's causing issues ..
37068         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37069         ul.on('click'   , this.onClick, this);
37070         
37071         
37072         ul.on("mouseover", this.onMouseOver, this);
37073         ul.on("mouseout", this.onMouseOut, this);
37074         this.items.each(function(item){
37075             if (item.hidden) {
37076                 return;
37077             }
37078             
37079             var li = document.createElement("li");
37080             li.className = "x-menu-list-item";
37081             ul.dom.appendChild(li);
37082             item.render(li, this);
37083         }, this);
37084         this.ul = ul;
37085         this.autoWidth();
37086     },
37087
37088     // private
37089     autoWidth : function(){
37090         var el = this.el, ul = this.ul;
37091         if(!el){
37092             return;
37093         }
37094         var w = this.width;
37095         if(w){
37096             el.setWidth(w);
37097         }else if(Roo.isIE){
37098             el.setWidth(this.minWidth);
37099             var t = el.dom.offsetWidth; // force recalc
37100             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37101         }
37102     },
37103
37104     // private
37105     delayAutoWidth : function(){
37106         if(this.rendered){
37107             if(!this.awTask){
37108                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37109             }
37110             this.awTask.delay(20);
37111         }
37112     },
37113
37114     // private
37115     findTargetItem : function(e){
37116         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37117         if(t && t.menuItemId){
37118             return this.items.get(t.menuItemId);
37119         }
37120     },
37121
37122     // private
37123     onClick : function(e){
37124         Roo.log("menu.onClick");
37125         var t = this.findTargetItem(e);
37126         if(!t){
37127             return;
37128         }
37129         Roo.log(e);
37130         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37131             if(t == this.activeItem && t.shouldDeactivate(e)){
37132                 this.activeItem.deactivate();
37133                 delete this.activeItem;
37134                 return;
37135             }
37136             if(t.canActivate){
37137                 this.setActiveItem(t, true);
37138             }
37139             return;
37140             
37141             
37142         }
37143         
37144         t.onClick(e);
37145         this.fireEvent("click", this, t, e);
37146     },
37147
37148     // private
37149     setActiveItem : function(item, autoExpand){
37150         if(item != this.activeItem){
37151             if(this.activeItem){
37152                 this.activeItem.deactivate();
37153             }
37154             this.activeItem = item;
37155             item.activate(autoExpand);
37156         }else if(autoExpand){
37157             item.expandMenu();
37158         }
37159     },
37160
37161     // private
37162     tryActivate : function(start, step){
37163         var items = this.items;
37164         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37165             var item = items.get(i);
37166             if(!item.disabled && item.canActivate){
37167                 this.setActiveItem(item, false);
37168                 return item;
37169             }
37170         }
37171         return false;
37172     },
37173
37174     // private
37175     onMouseOver : function(e){
37176         var t;
37177         if(t = this.findTargetItem(e)){
37178             if(t.canActivate && !t.disabled){
37179                 this.setActiveItem(t, true);
37180             }
37181         }
37182         this.fireEvent("mouseover", this, e, t);
37183     },
37184
37185     // private
37186     onMouseOut : function(e){
37187         var t;
37188         if(t = this.findTargetItem(e)){
37189             if(t == this.activeItem && t.shouldDeactivate(e)){
37190                 this.activeItem.deactivate();
37191                 delete this.activeItem;
37192             }
37193         }
37194         this.fireEvent("mouseout", this, e, t);
37195     },
37196
37197     /**
37198      * Read-only.  Returns true if the menu is currently displayed, else false.
37199      * @type Boolean
37200      */
37201     isVisible : function(){
37202         return this.el && !this.hidden;
37203     },
37204
37205     /**
37206      * Displays this menu relative to another element
37207      * @param {String/HTMLElement/Roo.Element} element The element to align to
37208      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37209      * the element (defaults to this.defaultAlign)
37210      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37211      */
37212     show : function(el, pos, parentMenu){
37213         this.parentMenu = parentMenu;
37214         if(!this.el){
37215             this.render();
37216         }
37217         this.fireEvent("beforeshow", this);
37218         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37219     },
37220
37221     /**
37222      * Displays this menu at a specific xy position
37223      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37224      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37225      */
37226     showAt : function(xy, parentMenu, /* private: */_e){
37227         this.parentMenu = parentMenu;
37228         if(!this.el){
37229             this.render();
37230         }
37231         if(_e !== false){
37232             this.fireEvent("beforeshow", this);
37233             xy = this.el.adjustForConstraints(xy);
37234         }
37235         this.el.setXY(xy);
37236         this.el.show();
37237         this.hidden = false;
37238         this.focus();
37239         this.fireEvent("show", this);
37240     },
37241
37242     focus : function(){
37243         if(!this.hidden){
37244             this.doFocus.defer(50, this);
37245         }
37246     },
37247
37248     doFocus : function(){
37249         if(!this.hidden){
37250             this.focusEl.focus();
37251         }
37252     },
37253
37254     /**
37255      * Hides this menu and optionally all parent menus
37256      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37257      */
37258     hide : function(deep){
37259         if(this.el && this.isVisible()){
37260             this.fireEvent("beforehide", this);
37261             if(this.activeItem){
37262                 this.activeItem.deactivate();
37263                 this.activeItem = null;
37264             }
37265             this.el.hide();
37266             this.hidden = true;
37267             this.fireEvent("hide", this);
37268         }
37269         if(deep === true && this.parentMenu){
37270             this.parentMenu.hide(true);
37271         }
37272     },
37273
37274     /**
37275      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37276      * Any of the following are valid:
37277      * <ul>
37278      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37279      * <li>An HTMLElement object which will be converted to a menu item</li>
37280      * <li>A menu item config object that will be created as a new menu item</li>
37281      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37282      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37283      * </ul>
37284      * Usage:
37285      * <pre><code>
37286 // Create the menu
37287 var menu = new Roo.menu.Menu();
37288
37289 // Create a menu item to add by reference
37290 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37291
37292 // Add a bunch of items at once using different methods.
37293 // Only the last item added will be returned.
37294 var item = menu.add(
37295     menuItem,                // add existing item by ref
37296     'Dynamic Item',          // new TextItem
37297     '-',                     // new separator
37298     { text: 'Config Item' }  // new item by config
37299 );
37300 </code></pre>
37301      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37302      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37303      */
37304     add : function(){
37305         var a = arguments, l = a.length, item;
37306         for(var i = 0; i < l; i++){
37307             var el = a[i];
37308             if ((typeof(el) == "object") && el.xtype && el.xns) {
37309                 el = Roo.factory(el, Roo.menu);
37310             }
37311             
37312             if(el.render){ // some kind of Item
37313                 item = this.addItem(el);
37314             }else if(typeof el == "string"){ // string
37315                 if(el == "separator" || el == "-"){
37316                     item = this.addSeparator();
37317                 }else{
37318                     item = this.addText(el);
37319                 }
37320             }else if(el.tagName || el.el){ // element
37321                 item = this.addElement(el);
37322             }else if(typeof el == "object"){ // must be menu item config?
37323                 item = this.addMenuItem(el);
37324             }
37325         }
37326         return item;
37327     },
37328
37329     /**
37330      * Returns this menu's underlying {@link Roo.Element} object
37331      * @return {Roo.Element} The element
37332      */
37333     getEl : function(){
37334         if(!this.el){
37335             this.render();
37336         }
37337         return this.el;
37338     },
37339
37340     /**
37341      * Adds a separator bar to the menu
37342      * @return {Roo.menu.Item} The menu item that was added
37343      */
37344     addSeparator : function(){
37345         return this.addItem(new Roo.menu.Separator());
37346     },
37347
37348     /**
37349      * Adds an {@link Roo.Element} object to the menu
37350      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37351      * @return {Roo.menu.Item} The menu item that was added
37352      */
37353     addElement : function(el){
37354         return this.addItem(new Roo.menu.BaseItem(el));
37355     },
37356
37357     /**
37358      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37359      * @param {Roo.menu.Item} item The menu item to add
37360      * @return {Roo.menu.Item} The menu item that was added
37361      */
37362     addItem : function(item){
37363         this.items.add(item);
37364         if(this.ul){
37365             var li = document.createElement("li");
37366             li.className = "x-menu-list-item";
37367             this.ul.dom.appendChild(li);
37368             item.render(li, this);
37369             this.delayAutoWidth();
37370         }
37371         return item;
37372     },
37373
37374     /**
37375      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37376      * @param {Object} config A MenuItem config object
37377      * @return {Roo.menu.Item} The menu item that was added
37378      */
37379     addMenuItem : function(config){
37380         if(!(config instanceof Roo.menu.Item)){
37381             if(typeof config.checked == "boolean"){ // must be check menu item config?
37382                 config = new Roo.menu.CheckItem(config);
37383             }else{
37384                 config = new Roo.menu.Item(config);
37385             }
37386         }
37387         return this.addItem(config);
37388     },
37389
37390     /**
37391      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37392      * @param {String} text The text to display in the menu item
37393      * @return {Roo.menu.Item} The menu item that was added
37394      */
37395     addText : function(text){
37396         return this.addItem(new Roo.menu.TextItem({ text : text }));
37397     },
37398
37399     /**
37400      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37401      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37402      * @param {Roo.menu.Item} item The menu item to add
37403      * @return {Roo.menu.Item} The menu item that was added
37404      */
37405     insert : function(index, item){
37406         this.items.insert(index, item);
37407         if(this.ul){
37408             var li = document.createElement("li");
37409             li.className = "x-menu-list-item";
37410             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37411             item.render(li, this);
37412             this.delayAutoWidth();
37413         }
37414         return item;
37415     },
37416
37417     /**
37418      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37419      * @param {Roo.menu.Item} item The menu item to remove
37420      */
37421     remove : function(item){
37422         this.items.removeKey(item.id);
37423         item.destroy();
37424     },
37425
37426     /**
37427      * Removes and destroys all items in the menu
37428      */
37429     removeAll : function(){
37430         var f;
37431         while(f = this.items.first()){
37432             this.remove(f);
37433         }
37434     }
37435 });
37436
37437 // MenuNav is a private utility class used internally by the Menu
37438 Roo.menu.MenuNav = function(menu){
37439     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37440     this.scope = this.menu = menu;
37441 };
37442
37443 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37444     doRelay : function(e, h){
37445         var k = e.getKey();
37446         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37447             this.menu.tryActivate(0, 1);
37448             return false;
37449         }
37450         return h.call(this.scope || this, e, this.menu);
37451     },
37452
37453     up : function(e, m){
37454         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37455             m.tryActivate(m.items.length-1, -1);
37456         }
37457     },
37458
37459     down : function(e, m){
37460         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37461             m.tryActivate(0, 1);
37462         }
37463     },
37464
37465     right : function(e, m){
37466         if(m.activeItem){
37467             m.activeItem.expandMenu(true);
37468         }
37469     },
37470
37471     left : function(e, m){
37472         m.hide();
37473         if(m.parentMenu && m.parentMenu.activeItem){
37474             m.parentMenu.activeItem.activate();
37475         }
37476     },
37477
37478     enter : function(e, m){
37479         if(m.activeItem){
37480             e.stopPropagation();
37481             m.activeItem.onClick(e);
37482             m.fireEvent("click", this, m.activeItem);
37483             return true;
37484         }
37485     }
37486 });/*
37487  * Based on:
37488  * Ext JS Library 1.1.1
37489  * Copyright(c) 2006-2007, Ext JS, LLC.
37490  *
37491  * Originally Released Under LGPL - original licence link has changed is not relivant.
37492  *
37493  * Fork - LGPL
37494  * <script type="text/javascript">
37495  */
37496  
37497 /**
37498  * @class Roo.menu.MenuMgr
37499  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37500  * @singleton
37501  */
37502 Roo.menu.MenuMgr = function(){
37503    var menus, active, groups = {}, attached = false, lastShow = new Date();
37504
37505    // private - called when first menu is created
37506    function init(){
37507        menus = {};
37508        active = new Roo.util.MixedCollection();
37509        Roo.get(document).addKeyListener(27, function(){
37510            if(active.length > 0){
37511                hideAll();
37512            }
37513        });
37514    }
37515
37516    // private
37517    function hideAll(){
37518        if(active && active.length > 0){
37519            var c = active.clone();
37520            c.each(function(m){
37521                m.hide();
37522            });
37523        }
37524    }
37525
37526    // private
37527    function onHide(m){
37528        active.remove(m);
37529        if(active.length < 1){
37530            Roo.get(document).un("mousedown", onMouseDown);
37531            attached = false;
37532        }
37533    }
37534
37535    // private
37536    function onShow(m){
37537        var last = active.last();
37538        lastShow = new Date();
37539        active.add(m);
37540        if(!attached){
37541            Roo.get(document).on("mousedown", onMouseDown);
37542            attached = true;
37543        }
37544        if(m.parentMenu){
37545           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37546           m.parentMenu.activeChild = m;
37547        }else if(last && last.isVisible()){
37548           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37549        }
37550    }
37551
37552    // private
37553    function onBeforeHide(m){
37554        if(m.activeChild){
37555            m.activeChild.hide();
37556        }
37557        if(m.autoHideTimer){
37558            clearTimeout(m.autoHideTimer);
37559            delete m.autoHideTimer;
37560        }
37561    }
37562
37563    // private
37564    function onBeforeShow(m){
37565        var pm = m.parentMenu;
37566        if(!pm && !m.allowOtherMenus){
37567            hideAll();
37568        }else if(pm && pm.activeChild && active != m){
37569            pm.activeChild.hide();
37570        }
37571    }
37572
37573    // private
37574    function onMouseDown(e){
37575        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37576            hideAll();
37577        }
37578    }
37579
37580    // private
37581    function onBeforeCheck(mi, state){
37582        if(state){
37583            var g = groups[mi.group];
37584            for(var i = 0, l = g.length; i < l; i++){
37585                if(g[i] != mi){
37586                    g[i].setChecked(false);
37587                }
37588            }
37589        }
37590    }
37591
37592    return {
37593
37594        /**
37595         * Hides all menus that are currently visible
37596         */
37597        hideAll : function(){
37598             hideAll();  
37599        },
37600
37601        // private
37602        register : function(menu){
37603            if(!menus){
37604                init();
37605            }
37606            menus[menu.id] = menu;
37607            menu.on("beforehide", onBeforeHide);
37608            menu.on("hide", onHide);
37609            menu.on("beforeshow", onBeforeShow);
37610            menu.on("show", onShow);
37611            var g = menu.group;
37612            if(g && menu.events["checkchange"]){
37613                if(!groups[g]){
37614                    groups[g] = [];
37615                }
37616                groups[g].push(menu);
37617                menu.on("checkchange", onCheck);
37618            }
37619        },
37620
37621         /**
37622          * Returns a {@link Roo.menu.Menu} object
37623          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37624          * be used to generate and return a new Menu instance.
37625          */
37626        get : function(menu){
37627            if(typeof menu == "string"){ // menu id
37628                return menus[menu];
37629            }else if(menu.events){  // menu instance
37630                return menu;
37631            }else if(typeof menu.length == 'number'){ // array of menu items?
37632                return new Roo.menu.Menu({items:menu});
37633            }else{ // otherwise, must be a config
37634                return new Roo.menu.Menu(menu);
37635            }
37636        },
37637
37638        // private
37639        unregister : function(menu){
37640            delete menus[menu.id];
37641            menu.un("beforehide", onBeforeHide);
37642            menu.un("hide", onHide);
37643            menu.un("beforeshow", onBeforeShow);
37644            menu.un("show", onShow);
37645            var g = menu.group;
37646            if(g && menu.events["checkchange"]){
37647                groups[g].remove(menu);
37648                menu.un("checkchange", onCheck);
37649            }
37650        },
37651
37652        // private
37653        registerCheckable : function(menuItem){
37654            var g = menuItem.group;
37655            if(g){
37656                if(!groups[g]){
37657                    groups[g] = [];
37658                }
37659                groups[g].push(menuItem);
37660                menuItem.on("beforecheckchange", onBeforeCheck);
37661            }
37662        },
37663
37664        // private
37665        unregisterCheckable : function(menuItem){
37666            var g = menuItem.group;
37667            if(g){
37668                groups[g].remove(menuItem);
37669                menuItem.un("beforecheckchange", onBeforeCheck);
37670            }
37671        }
37672    };
37673 }();/*
37674  * Based on:
37675  * Ext JS Library 1.1.1
37676  * Copyright(c) 2006-2007, Ext JS, LLC.
37677  *
37678  * Originally Released Under LGPL - original licence link has changed is not relivant.
37679  *
37680  * Fork - LGPL
37681  * <script type="text/javascript">
37682  */
37683  
37684
37685 /**
37686  * @class Roo.menu.BaseItem
37687  * @extends Roo.Component
37688  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37689  * management and base configuration options shared by all menu components.
37690  * @constructor
37691  * Creates a new BaseItem
37692  * @param {Object} config Configuration options
37693  */
37694 Roo.menu.BaseItem = function(config){
37695     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37696
37697     this.addEvents({
37698         /**
37699          * @event click
37700          * Fires when this item is clicked
37701          * @param {Roo.menu.BaseItem} this
37702          * @param {Roo.EventObject} e
37703          */
37704         click: true,
37705         /**
37706          * @event activate
37707          * Fires when this item is activated
37708          * @param {Roo.menu.BaseItem} this
37709          */
37710         activate : true,
37711         /**
37712          * @event deactivate
37713          * Fires when this item is deactivated
37714          * @param {Roo.menu.BaseItem} this
37715          */
37716         deactivate : true
37717     });
37718
37719     if(this.handler){
37720         this.on("click", this.handler, this.scope, true);
37721     }
37722 };
37723
37724 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37725     /**
37726      * @cfg {Function} handler
37727      * A function that will handle the click event of this menu item (defaults to undefined)
37728      */
37729     /**
37730      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37731      */
37732     canActivate : false,
37733     
37734      /**
37735      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37736      */
37737     hidden: false,
37738     
37739     /**
37740      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37741      */
37742     activeClass : "x-menu-item-active",
37743     /**
37744      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37745      */
37746     hideOnClick : true,
37747     /**
37748      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37749      */
37750     hideDelay : 100,
37751
37752     // private
37753     ctype: "Roo.menu.BaseItem",
37754
37755     // private
37756     actionMode : "container",
37757
37758     // private
37759     render : function(container, parentMenu){
37760         this.parentMenu = parentMenu;
37761         Roo.menu.BaseItem.superclass.render.call(this, container);
37762         this.container.menuItemId = this.id;
37763     },
37764
37765     // private
37766     onRender : function(container, position){
37767         this.el = Roo.get(this.el);
37768         container.dom.appendChild(this.el.dom);
37769     },
37770
37771     // private
37772     onClick : function(e){
37773         if(!this.disabled && this.fireEvent("click", this, e) !== false
37774                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37775             this.handleClick(e);
37776         }else{
37777             e.stopEvent();
37778         }
37779     },
37780
37781     // private
37782     activate : function(){
37783         if(this.disabled){
37784             return false;
37785         }
37786         var li = this.container;
37787         li.addClass(this.activeClass);
37788         this.region = li.getRegion().adjust(2, 2, -2, -2);
37789         this.fireEvent("activate", this);
37790         return true;
37791     },
37792
37793     // private
37794     deactivate : function(){
37795         this.container.removeClass(this.activeClass);
37796         this.fireEvent("deactivate", this);
37797     },
37798
37799     // private
37800     shouldDeactivate : function(e){
37801         return !this.region || !this.region.contains(e.getPoint());
37802     },
37803
37804     // private
37805     handleClick : function(e){
37806         if(this.hideOnClick){
37807             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37808         }
37809     },
37810
37811     // private
37812     expandMenu : function(autoActivate){
37813         // do nothing
37814     },
37815
37816     // private
37817     hideMenu : function(){
37818         // do nothing
37819     }
37820 });/*
37821  * Based on:
37822  * Ext JS Library 1.1.1
37823  * Copyright(c) 2006-2007, Ext JS, LLC.
37824  *
37825  * Originally Released Under LGPL - original licence link has changed is not relivant.
37826  *
37827  * Fork - LGPL
37828  * <script type="text/javascript">
37829  */
37830  
37831 /**
37832  * @class Roo.menu.Adapter
37833  * @extends Roo.menu.BaseItem
37834  * 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.
37835  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37836  * @constructor
37837  * Creates a new Adapter
37838  * @param {Object} config Configuration options
37839  */
37840 Roo.menu.Adapter = function(component, config){
37841     Roo.menu.Adapter.superclass.constructor.call(this, config);
37842     this.component = component;
37843 };
37844 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37845     // private
37846     canActivate : true,
37847
37848     // private
37849     onRender : function(container, position){
37850         this.component.render(container);
37851         this.el = this.component.getEl();
37852     },
37853
37854     // private
37855     activate : function(){
37856         if(this.disabled){
37857             return false;
37858         }
37859         this.component.focus();
37860         this.fireEvent("activate", this);
37861         return true;
37862     },
37863
37864     // private
37865     deactivate : function(){
37866         this.fireEvent("deactivate", this);
37867     },
37868
37869     // private
37870     disable : function(){
37871         this.component.disable();
37872         Roo.menu.Adapter.superclass.disable.call(this);
37873     },
37874
37875     // private
37876     enable : function(){
37877         this.component.enable();
37878         Roo.menu.Adapter.superclass.enable.call(this);
37879     }
37880 });/*
37881  * Based on:
37882  * Ext JS Library 1.1.1
37883  * Copyright(c) 2006-2007, Ext JS, LLC.
37884  *
37885  * Originally Released Under LGPL - original licence link has changed is not relivant.
37886  *
37887  * Fork - LGPL
37888  * <script type="text/javascript">
37889  */
37890
37891 /**
37892  * @class Roo.menu.TextItem
37893  * @extends Roo.menu.BaseItem
37894  * Adds a static text string to a menu, usually used as either a heading or group separator.
37895  * Note: old style constructor with text is still supported.
37896  * 
37897  * @constructor
37898  * Creates a new TextItem
37899  * @param {Object} cfg Configuration
37900  */
37901 Roo.menu.TextItem = function(cfg){
37902     if (typeof(cfg) == 'string') {
37903         this.text = cfg;
37904     } else {
37905         Roo.apply(this,cfg);
37906     }
37907     
37908     Roo.menu.TextItem.superclass.constructor.call(this);
37909 };
37910
37911 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
37912     /**
37913      * @cfg {Boolean} text Text to show on item.
37914      */
37915     text : '',
37916     
37917     /**
37918      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
37919      */
37920     hideOnClick : false,
37921     /**
37922      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
37923      */
37924     itemCls : "x-menu-text",
37925
37926     // private
37927     onRender : function(){
37928         var s = document.createElement("span");
37929         s.className = this.itemCls;
37930         s.innerHTML = this.text;
37931         this.el = s;
37932         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
37933     }
37934 });/*
37935  * Based on:
37936  * Ext JS Library 1.1.1
37937  * Copyright(c) 2006-2007, Ext JS, LLC.
37938  *
37939  * Originally Released Under LGPL - original licence link has changed is not relivant.
37940  *
37941  * Fork - LGPL
37942  * <script type="text/javascript">
37943  */
37944
37945 /**
37946  * @class Roo.menu.Separator
37947  * @extends Roo.menu.BaseItem
37948  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
37949  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
37950  * @constructor
37951  * @param {Object} config Configuration options
37952  */
37953 Roo.menu.Separator = function(config){
37954     Roo.menu.Separator.superclass.constructor.call(this, config);
37955 };
37956
37957 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
37958     /**
37959      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
37960      */
37961     itemCls : "x-menu-sep",
37962     /**
37963      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
37964      */
37965     hideOnClick : false,
37966
37967     // private
37968     onRender : function(li){
37969         var s = document.createElement("span");
37970         s.className = this.itemCls;
37971         s.innerHTML = "&#160;";
37972         this.el = s;
37973         li.addClass("x-menu-sep-li");
37974         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
37975     }
37976 });/*
37977  * Based on:
37978  * Ext JS Library 1.1.1
37979  * Copyright(c) 2006-2007, Ext JS, LLC.
37980  *
37981  * Originally Released Under LGPL - original licence link has changed is not relivant.
37982  *
37983  * Fork - LGPL
37984  * <script type="text/javascript">
37985  */
37986 /**
37987  * @class Roo.menu.Item
37988  * @extends Roo.menu.BaseItem
37989  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
37990  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
37991  * activation and click handling.
37992  * @constructor
37993  * Creates a new Item
37994  * @param {Object} config Configuration options
37995  */
37996 Roo.menu.Item = function(config){
37997     Roo.menu.Item.superclass.constructor.call(this, config);
37998     if(this.menu){
37999         this.menu = Roo.menu.MenuMgr.get(this.menu);
38000     }
38001 };
38002 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38003     
38004     /**
38005      * @cfg {String} text
38006      * The text to show on the menu item.
38007      */
38008     text: '',
38009      /**
38010      * @cfg {String} HTML to render in menu
38011      * The text to show on the menu item (HTML version).
38012      */
38013     html: '',
38014     /**
38015      * @cfg {String} icon
38016      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38017      */
38018     icon: undefined,
38019     /**
38020      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38021      */
38022     itemCls : "x-menu-item",
38023     /**
38024      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38025      */
38026     canActivate : true,
38027     /**
38028      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38029      */
38030     showDelay: 200,
38031     // doc'd in BaseItem
38032     hideDelay: 200,
38033
38034     // private
38035     ctype: "Roo.menu.Item",
38036     
38037     // private
38038     onRender : function(container, position){
38039         var el = document.createElement("a");
38040         el.hideFocus = true;
38041         el.unselectable = "on";
38042         el.href = this.href || "#";
38043         if(this.hrefTarget){
38044             el.target = this.hrefTarget;
38045         }
38046         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38047         
38048         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38049         
38050         el.innerHTML = String.format(
38051                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38052                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38053         this.el = el;
38054         Roo.menu.Item.superclass.onRender.call(this, container, position);
38055     },
38056
38057     /**
38058      * Sets the text to display in this menu item
38059      * @param {String} text The text to display
38060      * @param {Boolean} isHTML true to indicate text is pure html.
38061      */
38062     setText : function(text, isHTML){
38063         if (isHTML) {
38064             this.html = text;
38065         } else {
38066             this.text = text;
38067             this.html = '';
38068         }
38069         if(this.rendered){
38070             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38071      
38072             this.el.update(String.format(
38073                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38074                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38075             this.parentMenu.autoWidth();
38076         }
38077     },
38078
38079     // private
38080     handleClick : function(e){
38081         if(!this.href){ // if no link defined, stop the event automatically
38082             e.stopEvent();
38083         }
38084         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38085     },
38086
38087     // private
38088     activate : function(autoExpand){
38089         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38090             this.focus();
38091             if(autoExpand){
38092                 this.expandMenu();
38093             }
38094         }
38095         return true;
38096     },
38097
38098     // private
38099     shouldDeactivate : function(e){
38100         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38101             if(this.menu && this.menu.isVisible()){
38102                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38103             }
38104             return true;
38105         }
38106         return false;
38107     },
38108
38109     // private
38110     deactivate : function(){
38111         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38112         this.hideMenu();
38113     },
38114
38115     // private
38116     expandMenu : function(autoActivate){
38117         if(!this.disabled && this.menu){
38118             clearTimeout(this.hideTimer);
38119             delete this.hideTimer;
38120             if(!this.menu.isVisible() && !this.showTimer){
38121                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38122             }else if (this.menu.isVisible() && autoActivate){
38123                 this.menu.tryActivate(0, 1);
38124             }
38125         }
38126     },
38127
38128     // private
38129     deferExpand : function(autoActivate){
38130         delete this.showTimer;
38131         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38132         if(autoActivate){
38133             this.menu.tryActivate(0, 1);
38134         }
38135     },
38136
38137     // private
38138     hideMenu : function(){
38139         clearTimeout(this.showTimer);
38140         delete this.showTimer;
38141         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38142             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38143         }
38144     },
38145
38146     // private
38147     deferHide : function(){
38148         delete this.hideTimer;
38149         this.menu.hide();
38150     }
38151 });/*
38152  * Based on:
38153  * Ext JS Library 1.1.1
38154  * Copyright(c) 2006-2007, Ext JS, LLC.
38155  *
38156  * Originally Released Under LGPL - original licence link has changed is not relivant.
38157  *
38158  * Fork - LGPL
38159  * <script type="text/javascript">
38160  */
38161  
38162 /**
38163  * @class Roo.menu.CheckItem
38164  * @extends Roo.menu.Item
38165  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38166  * @constructor
38167  * Creates a new CheckItem
38168  * @param {Object} config Configuration options
38169  */
38170 Roo.menu.CheckItem = function(config){
38171     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38172     this.addEvents({
38173         /**
38174          * @event beforecheckchange
38175          * Fires before the checked value is set, providing an opportunity to cancel if needed
38176          * @param {Roo.menu.CheckItem} this
38177          * @param {Boolean} checked The new checked value that will be set
38178          */
38179         "beforecheckchange" : true,
38180         /**
38181          * @event checkchange
38182          * Fires after the checked value has been set
38183          * @param {Roo.menu.CheckItem} this
38184          * @param {Boolean} checked The checked value that was set
38185          */
38186         "checkchange" : true
38187     });
38188     if(this.checkHandler){
38189         this.on('checkchange', this.checkHandler, this.scope);
38190     }
38191 };
38192 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38193     /**
38194      * @cfg {String} group
38195      * All check items with the same group name will automatically be grouped into a single-select
38196      * radio button group (defaults to '')
38197      */
38198     /**
38199      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38200      */
38201     itemCls : "x-menu-item x-menu-check-item",
38202     /**
38203      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38204      */
38205     groupClass : "x-menu-group-item",
38206
38207     /**
38208      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38209      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38210      * initialized with checked = true will be rendered as checked.
38211      */
38212     checked: false,
38213
38214     // private
38215     ctype: "Roo.menu.CheckItem",
38216
38217     // private
38218     onRender : function(c){
38219         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38220         if(this.group){
38221             this.el.addClass(this.groupClass);
38222         }
38223         Roo.menu.MenuMgr.registerCheckable(this);
38224         if(this.checked){
38225             this.checked = false;
38226             this.setChecked(true, true);
38227         }
38228     },
38229
38230     // private
38231     destroy : function(){
38232         if(this.rendered){
38233             Roo.menu.MenuMgr.unregisterCheckable(this);
38234         }
38235         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38236     },
38237
38238     /**
38239      * Set the checked state of this item
38240      * @param {Boolean} checked The new checked value
38241      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38242      */
38243     setChecked : function(state, suppressEvent){
38244         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38245             if(this.container){
38246                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38247             }
38248             this.checked = state;
38249             if(suppressEvent !== true){
38250                 this.fireEvent("checkchange", this, state);
38251             }
38252         }
38253     },
38254
38255     // private
38256     handleClick : function(e){
38257        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38258            this.setChecked(!this.checked);
38259        }
38260        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38261     }
38262 });/*
38263  * Based on:
38264  * Ext JS Library 1.1.1
38265  * Copyright(c) 2006-2007, Ext JS, LLC.
38266  *
38267  * Originally Released Under LGPL - original licence link has changed is not relivant.
38268  *
38269  * Fork - LGPL
38270  * <script type="text/javascript">
38271  */
38272  
38273 /**
38274  * @class Roo.menu.DateItem
38275  * @extends Roo.menu.Adapter
38276  * A menu item that wraps the {@link Roo.DatPicker} component.
38277  * @constructor
38278  * Creates a new DateItem
38279  * @param {Object} config Configuration options
38280  */
38281 Roo.menu.DateItem = function(config){
38282     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38283     /** The Roo.DatePicker object @type Roo.DatePicker */
38284     this.picker = this.component;
38285     this.addEvents({select: true});
38286     
38287     this.picker.on("render", function(picker){
38288         picker.getEl().swallowEvent("click");
38289         picker.container.addClass("x-menu-date-item");
38290     });
38291
38292     this.picker.on("select", this.onSelect, this);
38293 };
38294
38295 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38296     // private
38297     onSelect : function(picker, date){
38298         this.fireEvent("select", this, date, picker);
38299         Roo.menu.DateItem.superclass.handleClick.call(this);
38300     }
38301 });/*
38302  * Based on:
38303  * Ext JS Library 1.1.1
38304  * Copyright(c) 2006-2007, Ext JS, LLC.
38305  *
38306  * Originally Released Under LGPL - original licence link has changed is not relivant.
38307  *
38308  * Fork - LGPL
38309  * <script type="text/javascript">
38310  */
38311  
38312 /**
38313  * @class Roo.menu.ColorItem
38314  * @extends Roo.menu.Adapter
38315  * A menu item that wraps the {@link Roo.ColorPalette} component.
38316  * @constructor
38317  * Creates a new ColorItem
38318  * @param {Object} config Configuration options
38319  */
38320 Roo.menu.ColorItem = function(config){
38321     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38322     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38323     this.palette = this.component;
38324     this.relayEvents(this.palette, ["select"]);
38325     if(this.selectHandler){
38326         this.on('select', this.selectHandler, this.scope);
38327     }
38328 };
38329 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38330  * Based on:
38331  * Ext JS Library 1.1.1
38332  * Copyright(c) 2006-2007, Ext JS, LLC.
38333  *
38334  * Originally Released Under LGPL - original licence link has changed is not relivant.
38335  *
38336  * Fork - LGPL
38337  * <script type="text/javascript">
38338  */
38339  
38340
38341 /**
38342  * @class Roo.menu.DateMenu
38343  * @extends Roo.menu.Menu
38344  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38345  * @constructor
38346  * Creates a new DateMenu
38347  * @param {Object} config Configuration options
38348  */
38349 Roo.menu.DateMenu = function(config){
38350     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38351     this.plain = true;
38352     var di = new Roo.menu.DateItem(config);
38353     this.add(di);
38354     /**
38355      * The {@link Roo.DatePicker} instance for this DateMenu
38356      * @type DatePicker
38357      */
38358     this.picker = di.picker;
38359     /**
38360      * @event select
38361      * @param {DatePicker} picker
38362      * @param {Date} date
38363      */
38364     this.relayEvents(di, ["select"]);
38365     this.on('beforeshow', function(){
38366         if(this.picker){
38367             this.picker.hideMonthPicker(false);
38368         }
38369     }, this);
38370 };
38371 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38372     cls:'x-date-menu'
38373 });/*
38374  * Based on:
38375  * Ext JS Library 1.1.1
38376  * Copyright(c) 2006-2007, Ext JS, LLC.
38377  *
38378  * Originally Released Under LGPL - original licence link has changed is not relivant.
38379  *
38380  * Fork - LGPL
38381  * <script type="text/javascript">
38382  */
38383  
38384
38385 /**
38386  * @class Roo.menu.ColorMenu
38387  * @extends Roo.menu.Menu
38388  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38389  * @constructor
38390  * Creates a new ColorMenu
38391  * @param {Object} config Configuration options
38392  */
38393 Roo.menu.ColorMenu = function(config){
38394     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38395     this.plain = true;
38396     var ci = new Roo.menu.ColorItem(config);
38397     this.add(ci);
38398     /**
38399      * The {@link Roo.ColorPalette} instance for this ColorMenu
38400      * @type ColorPalette
38401      */
38402     this.palette = ci.palette;
38403     /**
38404      * @event select
38405      * @param {ColorPalette} palette
38406      * @param {String} color
38407      */
38408     this.relayEvents(ci, ["select"]);
38409 };
38410 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38411  * Based on:
38412  * Ext JS Library 1.1.1
38413  * Copyright(c) 2006-2007, Ext JS, LLC.
38414  *
38415  * Originally Released Under LGPL - original licence link has changed is not relivant.
38416  *
38417  * Fork - LGPL
38418  * <script type="text/javascript">
38419  */
38420  
38421 /**
38422  * @class Roo.form.Field
38423  * @extends Roo.BoxComponent
38424  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38425  * @constructor
38426  * Creates a new Field
38427  * @param {Object} config Configuration options
38428  */
38429 Roo.form.Field = function(config){
38430     Roo.form.Field.superclass.constructor.call(this, config);
38431 };
38432
38433 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38434     /**
38435      * @cfg {String} fieldLabel Label to use when rendering a form.
38436      */
38437        /**
38438      * @cfg {String} qtip Mouse over tip
38439      */
38440      
38441     /**
38442      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38443      */
38444     invalidClass : "x-form-invalid",
38445     /**
38446      * @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")
38447      */
38448     invalidText : "The value in this field is invalid",
38449     /**
38450      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38451      */
38452     focusClass : "x-form-focus",
38453     /**
38454      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38455       automatic validation (defaults to "keyup").
38456      */
38457     validationEvent : "keyup",
38458     /**
38459      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38460      */
38461     validateOnBlur : true,
38462     /**
38463      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38464      */
38465     validationDelay : 250,
38466     /**
38467      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38468      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38469      */
38470     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38471     /**
38472      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38473      */
38474     fieldClass : "x-form-field",
38475     /**
38476      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38477      *<pre>
38478 Value         Description
38479 -----------   ----------------------------------------------------------------------
38480 qtip          Display a quick tip when the user hovers over the field
38481 title         Display a default browser title attribute popup
38482 under         Add a block div beneath the field containing the error text
38483 side          Add an error icon to the right of the field with a popup on hover
38484 [element id]  Add the error text directly to the innerHTML of the specified element
38485 </pre>
38486      */
38487     msgTarget : 'qtip',
38488     /**
38489      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38490      */
38491     msgFx : 'normal',
38492
38493     /**
38494      * @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.
38495      */
38496     readOnly : false,
38497
38498     /**
38499      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38500      */
38501     disabled : false,
38502
38503     /**
38504      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38505      */
38506     inputType : undefined,
38507     
38508     /**
38509      * @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).
38510          */
38511         tabIndex : undefined,
38512         
38513     // private
38514     isFormField : true,
38515
38516     // private
38517     hasFocus : false,
38518     /**
38519      * @property {Roo.Element} fieldEl
38520      * Element Containing the rendered Field (with label etc.)
38521      */
38522     /**
38523      * @cfg {Mixed} value A value to initialize this field with.
38524      */
38525     value : undefined,
38526
38527     /**
38528      * @cfg {String} name The field's HTML name attribute.
38529      */
38530     /**
38531      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38532      */
38533     // private
38534     loadedValue : false,
38535      
38536      
38537         // private ??
38538         initComponent : function(){
38539         Roo.form.Field.superclass.initComponent.call(this);
38540         this.addEvents({
38541             /**
38542              * @event focus
38543              * Fires when this field receives input focus.
38544              * @param {Roo.form.Field} this
38545              */
38546             focus : true,
38547             /**
38548              * @event blur
38549              * Fires when this field loses input focus.
38550              * @param {Roo.form.Field} this
38551              */
38552             blur : true,
38553             /**
38554              * @event specialkey
38555              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38556              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38557              * @param {Roo.form.Field} this
38558              * @param {Roo.EventObject} e The event object
38559              */
38560             specialkey : true,
38561             /**
38562              * @event change
38563              * Fires just before the field blurs if the field value has changed.
38564              * @param {Roo.form.Field} this
38565              * @param {Mixed} newValue The new value
38566              * @param {Mixed} oldValue The original value
38567              */
38568             change : true,
38569             /**
38570              * @event invalid
38571              * Fires after the field has been marked as invalid.
38572              * @param {Roo.form.Field} this
38573              * @param {String} msg The validation message
38574              */
38575             invalid : true,
38576             /**
38577              * @event valid
38578              * Fires after the field has been validated with no errors.
38579              * @param {Roo.form.Field} this
38580              */
38581             valid : true,
38582              /**
38583              * @event keyup
38584              * Fires after the key up
38585              * @param {Roo.form.Field} this
38586              * @param {Roo.EventObject}  e The event Object
38587              */
38588             keyup : true
38589         });
38590     },
38591
38592     /**
38593      * Returns the name attribute of the field if available
38594      * @return {String} name The field name
38595      */
38596     getName: function(){
38597          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38598     },
38599
38600     // private
38601     onRender : function(ct, position){
38602         Roo.form.Field.superclass.onRender.call(this, ct, position);
38603         if(!this.el){
38604             var cfg = this.getAutoCreate();
38605             if(!cfg.name){
38606                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38607             }
38608             if (!cfg.name.length) {
38609                 delete cfg.name;
38610             }
38611             if(this.inputType){
38612                 cfg.type = this.inputType;
38613             }
38614             this.el = ct.createChild(cfg, position);
38615         }
38616         var type = this.el.dom.type;
38617         if(type){
38618             if(type == 'password'){
38619                 type = 'text';
38620             }
38621             this.el.addClass('x-form-'+type);
38622         }
38623         if(this.readOnly){
38624             this.el.dom.readOnly = true;
38625         }
38626         if(this.tabIndex !== undefined){
38627             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38628         }
38629
38630         this.el.addClass([this.fieldClass, this.cls]);
38631         this.initValue();
38632     },
38633
38634     /**
38635      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38636      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38637      * @return {Roo.form.Field} this
38638      */
38639     applyTo : function(target){
38640         this.allowDomMove = false;
38641         this.el = Roo.get(target);
38642         this.render(this.el.dom.parentNode);
38643         return this;
38644     },
38645
38646     // private
38647     initValue : function(){
38648         if(this.value !== undefined){
38649             this.setValue(this.value);
38650         }else if(this.el.dom.value.length > 0){
38651             this.setValue(this.el.dom.value);
38652         }
38653     },
38654
38655     /**
38656      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38657      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38658      */
38659     isDirty : function() {
38660         if(this.disabled) {
38661             return false;
38662         }
38663         return String(this.getValue()) !== String(this.originalValue);
38664     },
38665
38666     /**
38667      * stores the current value in loadedValue
38668      */
38669     resetHasChanged : function()
38670     {
38671         this.loadedValue = String(this.getValue());
38672     },
38673     /**
38674      * checks the current value against the 'loaded' value.
38675      * Note - will return false if 'resetHasChanged' has not been called first.
38676      */
38677     hasChanged : function()
38678     {
38679         if(this.disabled || this.readOnly) {
38680             return false;
38681         }
38682         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38683     },
38684     
38685     
38686     
38687     // private
38688     afterRender : function(){
38689         Roo.form.Field.superclass.afterRender.call(this);
38690         this.initEvents();
38691     },
38692
38693     // private
38694     fireKey : function(e){
38695         //Roo.log('field ' + e.getKey());
38696         if(e.isNavKeyPress()){
38697             this.fireEvent("specialkey", this, e);
38698         }
38699     },
38700
38701     /**
38702      * Resets the current field value to the originally loaded value and clears any validation messages
38703      */
38704     reset : function(){
38705         this.setValue(this.resetValue);
38706         this.clearInvalid();
38707     },
38708
38709     // private
38710     initEvents : function(){
38711         // safari killled keypress - so keydown is now used..
38712         this.el.on("keydown" , this.fireKey,  this);
38713         this.el.on("focus", this.onFocus,  this);
38714         this.el.on("blur", this.onBlur,  this);
38715         this.el.relayEvent('keyup', this);
38716
38717         // reference to original value for reset
38718         this.originalValue = this.getValue();
38719         this.resetValue =  this.getValue();
38720     },
38721
38722     // private
38723     onFocus : function(){
38724         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38725             this.el.addClass(this.focusClass);
38726         }
38727         if(!this.hasFocus){
38728             this.hasFocus = true;
38729             this.startValue = this.getValue();
38730             this.fireEvent("focus", this);
38731         }
38732     },
38733
38734     beforeBlur : Roo.emptyFn,
38735
38736     // private
38737     onBlur : function(){
38738         this.beforeBlur();
38739         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38740             this.el.removeClass(this.focusClass);
38741         }
38742         this.hasFocus = false;
38743         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38744             this.validate();
38745         }
38746         var v = this.getValue();
38747         if(String(v) !== String(this.startValue)){
38748             this.fireEvent('change', this, v, this.startValue);
38749         }
38750         this.fireEvent("blur", this);
38751     },
38752
38753     /**
38754      * Returns whether or not the field value is currently valid
38755      * @param {Boolean} preventMark True to disable marking the field invalid
38756      * @return {Boolean} True if the value is valid, else false
38757      */
38758     isValid : function(preventMark){
38759         if(this.disabled){
38760             return true;
38761         }
38762         var restore = this.preventMark;
38763         this.preventMark = preventMark === true;
38764         var v = this.validateValue(this.processValue(this.getRawValue()));
38765         this.preventMark = restore;
38766         return v;
38767     },
38768
38769     /**
38770      * Validates the field value
38771      * @return {Boolean} True if the value is valid, else false
38772      */
38773     validate : function(){
38774         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38775             this.clearInvalid();
38776             return true;
38777         }
38778         return false;
38779     },
38780
38781     processValue : function(value){
38782         return value;
38783     },
38784
38785     // private
38786     // Subclasses should provide the validation implementation by overriding this
38787     validateValue : function(value){
38788         return true;
38789     },
38790
38791     /**
38792      * Mark this field as invalid
38793      * @param {String} msg The validation message
38794      */
38795     markInvalid : function(msg){
38796         if(!this.rendered || this.preventMark){ // not rendered
38797             return;
38798         }
38799         
38800         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38801         
38802         obj.el.addClass(this.invalidClass);
38803         msg = msg || this.invalidText;
38804         switch(this.msgTarget){
38805             case 'qtip':
38806                 obj.el.dom.qtip = msg;
38807                 obj.el.dom.qclass = 'x-form-invalid-tip';
38808                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38809                     Roo.QuickTips.enable();
38810                 }
38811                 break;
38812             case 'title':
38813                 this.el.dom.title = msg;
38814                 break;
38815             case 'under':
38816                 if(!this.errorEl){
38817                     var elp = this.el.findParent('.x-form-element', 5, true);
38818                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38819                     this.errorEl.setWidth(elp.getWidth(true)-20);
38820                 }
38821                 this.errorEl.update(msg);
38822                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38823                 break;
38824             case 'side':
38825                 if(!this.errorIcon){
38826                     var elp = this.el.findParent('.x-form-element', 5, true);
38827                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38828                 }
38829                 this.alignErrorIcon();
38830                 this.errorIcon.dom.qtip = msg;
38831                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38832                 this.errorIcon.show();
38833                 this.on('resize', this.alignErrorIcon, this);
38834                 break;
38835             default:
38836                 var t = Roo.getDom(this.msgTarget);
38837                 t.innerHTML = msg;
38838                 t.style.display = this.msgDisplay;
38839                 break;
38840         }
38841         this.fireEvent('invalid', this, msg);
38842     },
38843
38844     // private
38845     alignErrorIcon : function(){
38846         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38847     },
38848
38849     /**
38850      * Clear any invalid styles/messages for this field
38851      */
38852     clearInvalid : function(){
38853         if(!this.rendered || this.preventMark){ // not rendered
38854             return;
38855         }
38856         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38857         
38858         obj.el.removeClass(this.invalidClass);
38859         switch(this.msgTarget){
38860             case 'qtip':
38861                 obj.el.dom.qtip = '';
38862                 break;
38863             case 'title':
38864                 this.el.dom.title = '';
38865                 break;
38866             case 'under':
38867                 if(this.errorEl){
38868                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
38869                 }
38870                 break;
38871             case 'side':
38872                 if(this.errorIcon){
38873                     this.errorIcon.dom.qtip = '';
38874                     this.errorIcon.hide();
38875                     this.un('resize', this.alignErrorIcon, this);
38876                 }
38877                 break;
38878             default:
38879                 var t = Roo.getDom(this.msgTarget);
38880                 t.innerHTML = '';
38881                 t.style.display = 'none';
38882                 break;
38883         }
38884         this.fireEvent('valid', this);
38885     },
38886
38887     /**
38888      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
38889      * @return {Mixed} value The field value
38890      */
38891     getRawValue : function(){
38892         var v = this.el.getValue();
38893         
38894         return v;
38895     },
38896
38897     /**
38898      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
38899      * @return {Mixed} value The field value
38900      */
38901     getValue : function(){
38902         var v = this.el.getValue();
38903          
38904         return v;
38905     },
38906
38907     /**
38908      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
38909      * @param {Mixed} value The value to set
38910      */
38911     setRawValue : function(v){
38912         return this.el.dom.value = (v === null || v === undefined ? '' : v);
38913     },
38914
38915     /**
38916      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
38917      * @param {Mixed} value The value to set
38918      */
38919     setValue : function(v){
38920         this.value = v;
38921         if(this.rendered){
38922             this.el.dom.value = (v === null || v === undefined ? '' : v);
38923              this.validate();
38924         }
38925     },
38926
38927     adjustSize : function(w, h){
38928         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
38929         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
38930         return s;
38931     },
38932
38933     adjustWidth : function(tag, w){
38934         tag = tag.toLowerCase();
38935         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
38936             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
38937                 if(tag == 'input'){
38938                     return w + 2;
38939                 }
38940                 if(tag == 'textarea'){
38941                     return w-2;
38942                 }
38943             }else if(Roo.isOpera){
38944                 if(tag == 'input'){
38945                     return w + 2;
38946                 }
38947                 if(tag == 'textarea'){
38948                     return w-2;
38949                 }
38950             }
38951         }
38952         return w;
38953     }
38954 });
38955
38956
38957 // anything other than normal should be considered experimental
38958 Roo.form.Field.msgFx = {
38959     normal : {
38960         show: function(msgEl, f){
38961             msgEl.setDisplayed('block');
38962         },
38963
38964         hide : function(msgEl, f){
38965             msgEl.setDisplayed(false).update('');
38966         }
38967     },
38968
38969     slide : {
38970         show: function(msgEl, f){
38971             msgEl.slideIn('t', {stopFx:true});
38972         },
38973
38974         hide : function(msgEl, f){
38975             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
38976         }
38977     },
38978
38979     slideRight : {
38980         show: function(msgEl, f){
38981             msgEl.fixDisplay();
38982             msgEl.alignTo(f.el, 'tl-tr');
38983             msgEl.slideIn('l', {stopFx:true});
38984         },
38985
38986         hide : function(msgEl, f){
38987             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
38988         }
38989     }
38990 };/*
38991  * Based on:
38992  * Ext JS Library 1.1.1
38993  * Copyright(c) 2006-2007, Ext JS, LLC.
38994  *
38995  * Originally Released Under LGPL - original licence link has changed is not relivant.
38996  *
38997  * Fork - LGPL
38998  * <script type="text/javascript">
38999  */
39000  
39001
39002 /**
39003  * @class Roo.form.TextField
39004  * @extends Roo.form.Field
39005  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39006  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39007  * @constructor
39008  * Creates a new TextField
39009  * @param {Object} config Configuration options
39010  */
39011 Roo.form.TextField = function(config){
39012     Roo.form.TextField.superclass.constructor.call(this, config);
39013     this.addEvents({
39014         /**
39015          * @event autosize
39016          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39017          * according to the default logic, but this event provides a hook for the developer to apply additional
39018          * logic at runtime to resize the field if needed.
39019              * @param {Roo.form.Field} this This text field
39020              * @param {Number} width The new field width
39021              */
39022         autosize : true
39023     });
39024 };
39025
39026 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39027     /**
39028      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39029      */
39030     grow : false,
39031     /**
39032      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39033      */
39034     growMin : 30,
39035     /**
39036      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39037      */
39038     growMax : 800,
39039     /**
39040      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39041      */
39042     vtype : null,
39043     /**
39044      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39045      */
39046     maskRe : null,
39047     /**
39048      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39049      */
39050     disableKeyFilter : false,
39051     /**
39052      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39053      */
39054     allowBlank : true,
39055     /**
39056      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39057      */
39058     minLength : 0,
39059     /**
39060      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39061      */
39062     maxLength : Number.MAX_VALUE,
39063     /**
39064      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39065      */
39066     minLengthText : "The minimum length for this field is {0}",
39067     /**
39068      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39069      */
39070     maxLengthText : "The maximum length for this field is {0}",
39071     /**
39072      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39073      */
39074     selectOnFocus : false,
39075     /**
39076      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39077      */
39078     blankText : "This field is required",
39079     /**
39080      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39081      * If available, this function will be called only after the basic validators all return true, and will be passed the
39082      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39083      */
39084     validator : null,
39085     /**
39086      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39087      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39088      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39089      */
39090     regex : null,
39091     /**
39092      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39093      */
39094     regexText : "",
39095     /**
39096      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39097      */
39098     emptyText : null,
39099    
39100
39101     // private
39102     initEvents : function()
39103     {
39104         if (this.emptyText) {
39105             this.el.attr('placeholder', this.emptyText);
39106         }
39107         
39108         Roo.form.TextField.superclass.initEvents.call(this);
39109         if(this.validationEvent == 'keyup'){
39110             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39111             this.el.on('keyup', this.filterValidation, this);
39112         }
39113         else if(this.validationEvent !== false){
39114             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39115         }
39116         
39117         if(this.selectOnFocus){
39118             this.on("focus", this.preFocus, this);
39119             
39120         }
39121         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39122             this.el.on("keypress", this.filterKeys, this);
39123         }
39124         if(this.grow){
39125             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39126             this.el.on("click", this.autoSize,  this);
39127         }
39128         if(this.el.is('input[type=password]') && Roo.isSafari){
39129             this.el.on('keydown', this.SafariOnKeyDown, this);
39130         }
39131     },
39132
39133     processValue : function(value){
39134         if(this.stripCharsRe){
39135             var newValue = value.replace(this.stripCharsRe, '');
39136             if(newValue !== value){
39137                 this.setRawValue(newValue);
39138                 return newValue;
39139             }
39140         }
39141         return value;
39142     },
39143
39144     filterValidation : function(e){
39145         if(!e.isNavKeyPress()){
39146             this.validationTask.delay(this.validationDelay);
39147         }
39148     },
39149
39150     // private
39151     onKeyUp : function(e){
39152         if(!e.isNavKeyPress()){
39153             this.autoSize();
39154         }
39155     },
39156
39157     /**
39158      * Resets the current field value to the originally-loaded value and clears any validation messages.
39159      *  
39160      */
39161     reset : function(){
39162         Roo.form.TextField.superclass.reset.call(this);
39163        
39164     },
39165
39166     
39167     // private
39168     preFocus : function(){
39169         
39170         if(this.selectOnFocus){
39171             this.el.dom.select();
39172         }
39173     },
39174
39175     
39176     // private
39177     filterKeys : function(e){
39178         var k = e.getKey();
39179         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39180             return;
39181         }
39182         var c = e.getCharCode(), cc = String.fromCharCode(c);
39183         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39184             return;
39185         }
39186         if(!this.maskRe.test(cc)){
39187             e.stopEvent();
39188         }
39189     },
39190
39191     setValue : function(v){
39192         
39193         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39194         
39195         this.autoSize();
39196     },
39197
39198     /**
39199      * Validates a value according to the field's validation rules and marks the field as invalid
39200      * if the validation fails
39201      * @param {Mixed} value The value to validate
39202      * @return {Boolean} True if the value is valid, else false
39203      */
39204     validateValue : function(value){
39205         if(value.length < 1)  { // if it's blank
39206              if(this.allowBlank){
39207                 this.clearInvalid();
39208                 return true;
39209              }else{
39210                 this.markInvalid(this.blankText);
39211                 return false;
39212              }
39213         }
39214         if(value.length < this.minLength){
39215             this.markInvalid(String.format(this.minLengthText, this.minLength));
39216             return false;
39217         }
39218         if(value.length > this.maxLength){
39219             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39220             return false;
39221         }
39222         if(this.vtype){
39223             var vt = Roo.form.VTypes;
39224             if(!vt[this.vtype](value, this)){
39225                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39226                 return false;
39227             }
39228         }
39229         if(typeof this.validator == "function"){
39230             var msg = this.validator(value);
39231             if(msg !== true){
39232                 this.markInvalid(msg);
39233                 return false;
39234             }
39235         }
39236         if(this.regex && !this.regex.test(value)){
39237             this.markInvalid(this.regexText);
39238             return false;
39239         }
39240         return true;
39241     },
39242
39243     /**
39244      * Selects text in this field
39245      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39246      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39247      */
39248     selectText : function(start, end){
39249         var v = this.getRawValue();
39250         if(v.length > 0){
39251             start = start === undefined ? 0 : start;
39252             end = end === undefined ? v.length : end;
39253             var d = this.el.dom;
39254             if(d.setSelectionRange){
39255                 d.setSelectionRange(start, end);
39256             }else if(d.createTextRange){
39257                 var range = d.createTextRange();
39258                 range.moveStart("character", start);
39259                 range.moveEnd("character", v.length-end);
39260                 range.select();
39261             }
39262         }
39263     },
39264
39265     /**
39266      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39267      * This only takes effect if grow = true, and fires the autosize event.
39268      */
39269     autoSize : function(){
39270         if(!this.grow || !this.rendered){
39271             return;
39272         }
39273         if(!this.metrics){
39274             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39275         }
39276         var el = this.el;
39277         var v = el.dom.value;
39278         var d = document.createElement('div');
39279         d.appendChild(document.createTextNode(v));
39280         v = d.innerHTML;
39281         d = null;
39282         v += "&#160;";
39283         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39284         this.el.setWidth(w);
39285         this.fireEvent("autosize", this, w);
39286     },
39287     
39288     // private
39289     SafariOnKeyDown : function(event)
39290     {
39291         // this is a workaround for a password hang bug on chrome/ webkit.
39292         
39293         var isSelectAll = false;
39294         
39295         if(this.el.dom.selectionEnd > 0){
39296             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39297         }
39298         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39299             event.preventDefault();
39300             this.setValue('');
39301             return;
39302         }
39303         
39304         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39305             
39306             event.preventDefault();
39307             // this is very hacky as keydown always get's upper case.
39308             
39309             var cc = String.fromCharCode(event.getCharCode());
39310             
39311             
39312             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39313             
39314         }
39315         
39316         
39317     }
39318 });/*
39319  * Based on:
39320  * Ext JS Library 1.1.1
39321  * Copyright(c) 2006-2007, Ext JS, LLC.
39322  *
39323  * Originally Released Under LGPL - original licence link has changed is not relivant.
39324  *
39325  * Fork - LGPL
39326  * <script type="text/javascript">
39327  */
39328  
39329 /**
39330  * @class Roo.form.Hidden
39331  * @extends Roo.form.TextField
39332  * Simple Hidden element used on forms 
39333  * 
39334  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39335  * 
39336  * @constructor
39337  * Creates a new Hidden form element.
39338  * @param {Object} config Configuration options
39339  */
39340
39341
39342
39343 // easy hidden field...
39344 Roo.form.Hidden = function(config){
39345     Roo.form.Hidden.superclass.constructor.call(this, config);
39346 };
39347   
39348 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39349     fieldLabel:      '',
39350     inputType:      'hidden',
39351     width:          50,
39352     allowBlank:     true,
39353     labelSeparator: '',
39354     hidden:         true,
39355     itemCls :       'x-form-item-display-none'
39356
39357
39358 });
39359
39360
39361 /*
39362  * Based on:
39363  * Ext JS Library 1.1.1
39364  * Copyright(c) 2006-2007, Ext JS, LLC.
39365  *
39366  * Originally Released Under LGPL - original licence link has changed is not relivant.
39367  *
39368  * Fork - LGPL
39369  * <script type="text/javascript">
39370  */
39371  
39372 /**
39373  * @class Roo.form.TriggerField
39374  * @extends Roo.form.TextField
39375  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39376  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39377  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39378  * for which you can provide a custom implementation.  For example:
39379  * <pre><code>
39380 var trigger = new Roo.form.TriggerField();
39381 trigger.onTriggerClick = myTriggerFn;
39382 trigger.applyTo('my-field');
39383 </code></pre>
39384  *
39385  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39386  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39387  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39388  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39389  * @constructor
39390  * Create a new TriggerField.
39391  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39392  * to the base TextField)
39393  */
39394 Roo.form.TriggerField = function(config){
39395     this.mimicing = false;
39396     Roo.form.TriggerField.superclass.constructor.call(this, config);
39397 };
39398
39399 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39400     /**
39401      * @cfg {String} triggerClass A CSS class to apply to the trigger
39402      */
39403     /**
39404      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39405      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39406      */
39407     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39408     /**
39409      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39410      */
39411     hideTrigger:false,
39412
39413     /** @cfg {Boolean} grow @hide */
39414     /** @cfg {Number} growMin @hide */
39415     /** @cfg {Number} growMax @hide */
39416
39417     /**
39418      * @hide 
39419      * @method
39420      */
39421     autoSize: Roo.emptyFn,
39422     // private
39423     monitorTab : true,
39424     // private
39425     deferHeight : true,
39426
39427     
39428     actionMode : 'wrap',
39429     // private
39430     onResize : function(w, h){
39431         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39432         if(typeof w == 'number'){
39433             var x = w - this.trigger.getWidth();
39434             this.el.setWidth(this.adjustWidth('input', x));
39435             this.trigger.setStyle('left', x+'px');
39436         }
39437     },
39438
39439     // private
39440     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39441
39442     // private
39443     getResizeEl : function(){
39444         return this.wrap;
39445     },
39446
39447     // private
39448     getPositionEl : function(){
39449         return this.wrap;
39450     },
39451
39452     // private
39453     alignErrorIcon : function(){
39454         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39455     },
39456
39457     // private
39458     onRender : function(ct, position){
39459         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39460         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39461         this.trigger = this.wrap.createChild(this.triggerConfig ||
39462                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39463         if(this.hideTrigger){
39464             this.trigger.setDisplayed(false);
39465         }
39466         this.initTrigger();
39467         if(!this.width){
39468             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39469         }
39470     },
39471
39472     // private
39473     initTrigger : function(){
39474         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39475         this.trigger.addClassOnOver('x-form-trigger-over');
39476         this.trigger.addClassOnClick('x-form-trigger-click');
39477     },
39478
39479     // private
39480     onDestroy : function(){
39481         if(this.trigger){
39482             this.trigger.removeAllListeners();
39483             this.trigger.remove();
39484         }
39485         if(this.wrap){
39486             this.wrap.remove();
39487         }
39488         Roo.form.TriggerField.superclass.onDestroy.call(this);
39489     },
39490
39491     // private
39492     onFocus : function(){
39493         Roo.form.TriggerField.superclass.onFocus.call(this);
39494         if(!this.mimicing){
39495             this.wrap.addClass('x-trigger-wrap-focus');
39496             this.mimicing = true;
39497             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39498             if(this.monitorTab){
39499                 this.el.on("keydown", this.checkTab, this);
39500             }
39501         }
39502     },
39503
39504     // private
39505     checkTab : function(e){
39506         if(e.getKey() == e.TAB){
39507             this.triggerBlur();
39508         }
39509     },
39510
39511     // private
39512     onBlur : function(){
39513         // do nothing
39514     },
39515
39516     // private
39517     mimicBlur : function(e, t){
39518         if(!this.wrap.contains(t) && this.validateBlur()){
39519             this.triggerBlur();
39520         }
39521     },
39522
39523     // private
39524     triggerBlur : function(){
39525         this.mimicing = false;
39526         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39527         if(this.monitorTab){
39528             this.el.un("keydown", this.checkTab, this);
39529         }
39530         this.wrap.removeClass('x-trigger-wrap-focus');
39531         Roo.form.TriggerField.superclass.onBlur.call(this);
39532     },
39533
39534     // private
39535     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39536     validateBlur : function(e, t){
39537         return true;
39538     },
39539
39540     // private
39541     onDisable : function(){
39542         Roo.form.TriggerField.superclass.onDisable.call(this);
39543         if(this.wrap){
39544             this.wrap.addClass('x-item-disabled');
39545         }
39546     },
39547
39548     // private
39549     onEnable : function(){
39550         Roo.form.TriggerField.superclass.onEnable.call(this);
39551         if(this.wrap){
39552             this.wrap.removeClass('x-item-disabled');
39553         }
39554     },
39555
39556     // private
39557     onShow : function(){
39558         var ae = this.getActionEl();
39559         
39560         if(ae){
39561             ae.dom.style.display = '';
39562             ae.dom.style.visibility = 'visible';
39563         }
39564     },
39565
39566     // private
39567     
39568     onHide : function(){
39569         var ae = this.getActionEl();
39570         ae.dom.style.display = 'none';
39571     },
39572
39573     /**
39574      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39575      * by an implementing function.
39576      * @method
39577      * @param {EventObject} e
39578      */
39579     onTriggerClick : Roo.emptyFn
39580 });
39581
39582 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39583 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39584 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39585 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39586     initComponent : function(){
39587         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39588
39589         this.triggerConfig = {
39590             tag:'span', cls:'x-form-twin-triggers', cn:[
39591             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39592             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39593         ]};
39594     },
39595
39596     getTrigger : function(index){
39597         return this.triggers[index];
39598     },
39599
39600     initTrigger : function(){
39601         var ts = this.trigger.select('.x-form-trigger', true);
39602         this.wrap.setStyle('overflow', 'hidden');
39603         var triggerField = this;
39604         ts.each(function(t, all, index){
39605             t.hide = function(){
39606                 var w = triggerField.wrap.getWidth();
39607                 this.dom.style.display = 'none';
39608                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39609             };
39610             t.show = function(){
39611                 var w = triggerField.wrap.getWidth();
39612                 this.dom.style.display = '';
39613                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39614             };
39615             var triggerIndex = 'Trigger'+(index+1);
39616
39617             if(this['hide'+triggerIndex]){
39618                 t.dom.style.display = 'none';
39619             }
39620             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39621             t.addClassOnOver('x-form-trigger-over');
39622             t.addClassOnClick('x-form-trigger-click');
39623         }, this);
39624         this.triggers = ts.elements;
39625     },
39626
39627     onTrigger1Click : Roo.emptyFn,
39628     onTrigger2Click : Roo.emptyFn
39629 });/*
39630  * Based on:
39631  * Ext JS Library 1.1.1
39632  * Copyright(c) 2006-2007, Ext JS, LLC.
39633  *
39634  * Originally Released Under LGPL - original licence link has changed is not relivant.
39635  *
39636  * Fork - LGPL
39637  * <script type="text/javascript">
39638  */
39639  
39640 /**
39641  * @class Roo.form.TextArea
39642  * @extends Roo.form.TextField
39643  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39644  * support for auto-sizing.
39645  * @constructor
39646  * Creates a new TextArea
39647  * @param {Object} config Configuration options
39648  */
39649 Roo.form.TextArea = function(config){
39650     Roo.form.TextArea.superclass.constructor.call(this, config);
39651     // these are provided exchanges for backwards compat
39652     // minHeight/maxHeight were replaced by growMin/growMax to be
39653     // compatible with TextField growing config values
39654     if(this.minHeight !== undefined){
39655         this.growMin = this.minHeight;
39656     }
39657     if(this.maxHeight !== undefined){
39658         this.growMax = this.maxHeight;
39659     }
39660 };
39661
39662 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39663     /**
39664      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39665      */
39666     growMin : 60,
39667     /**
39668      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39669      */
39670     growMax: 1000,
39671     /**
39672      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39673      * in the field (equivalent to setting overflow: hidden, defaults to false)
39674      */
39675     preventScrollbars: false,
39676     /**
39677      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39678      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39679      */
39680
39681     // private
39682     onRender : function(ct, position){
39683         if(!this.el){
39684             this.defaultAutoCreate = {
39685                 tag: "textarea",
39686                 style:"width:300px;height:60px;",
39687                 autocomplete: "new-password"
39688             };
39689         }
39690         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39691         if(this.grow){
39692             this.textSizeEl = Roo.DomHelper.append(document.body, {
39693                 tag: "pre", cls: "x-form-grow-sizer"
39694             });
39695             if(this.preventScrollbars){
39696                 this.el.setStyle("overflow", "hidden");
39697             }
39698             this.el.setHeight(this.growMin);
39699         }
39700     },
39701
39702     onDestroy : function(){
39703         if(this.textSizeEl){
39704             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39705         }
39706         Roo.form.TextArea.superclass.onDestroy.call(this);
39707     },
39708
39709     // private
39710     onKeyUp : function(e){
39711         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39712             this.autoSize();
39713         }
39714     },
39715
39716     /**
39717      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39718      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39719      */
39720     autoSize : function(){
39721         if(!this.grow || !this.textSizeEl){
39722             return;
39723         }
39724         var el = this.el;
39725         var v = el.dom.value;
39726         var ts = this.textSizeEl;
39727
39728         ts.innerHTML = '';
39729         ts.appendChild(document.createTextNode(v));
39730         v = ts.innerHTML;
39731
39732         Roo.fly(ts).setWidth(this.el.getWidth());
39733         if(v.length < 1){
39734             v = "&#160;&#160;";
39735         }else{
39736             if(Roo.isIE){
39737                 v = v.replace(/\n/g, '<p>&#160;</p>');
39738             }
39739             v += "&#160;\n&#160;";
39740         }
39741         ts.innerHTML = v;
39742         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39743         if(h != this.lastHeight){
39744             this.lastHeight = h;
39745             this.el.setHeight(h);
39746             this.fireEvent("autosize", this, h);
39747         }
39748     }
39749 });/*
39750  * Based on:
39751  * Ext JS Library 1.1.1
39752  * Copyright(c) 2006-2007, Ext JS, LLC.
39753  *
39754  * Originally Released Under LGPL - original licence link has changed is not relivant.
39755  *
39756  * Fork - LGPL
39757  * <script type="text/javascript">
39758  */
39759  
39760
39761 /**
39762  * @class Roo.form.NumberField
39763  * @extends Roo.form.TextField
39764  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39765  * @constructor
39766  * Creates a new NumberField
39767  * @param {Object} config Configuration options
39768  */
39769 Roo.form.NumberField = function(config){
39770     Roo.form.NumberField.superclass.constructor.call(this, config);
39771 };
39772
39773 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39774     /**
39775      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39776      */
39777     fieldClass: "x-form-field x-form-num-field",
39778     /**
39779      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39780      */
39781     allowDecimals : true,
39782     /**
39783      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39784      */
39785     decimalSeparator : ".",
39786     /**
39787      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39788      */
39789     decimalPrecision : 2,
39790     /**
39791      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39792      */
39793     allowNegative : true,
39794     /**
39795      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39796      */
39797     minValue : Number.NEGATIVE_INFINITY,
39798     /**
39799      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39800      */
39801     maxValue : Number.MAX_VALUE,
39802     /**
39803      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39804      */
39805     minText : "The minimum value for this field is {0}",
39806     /**
39807      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39808      */
39809     maxText : "The maximum value for this field is {0}",
39810     /**
39811      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39812      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39813      */
39814     nanText : "{0} is not a valid number",
39815
39816     // private
39817     initEvents : function(){
39818         Roo.form.NumberField.superclass.initEvents.call(this);
39819         var allowed = "0123456789";
39820         if(this.allowDecimals){
39821             allowed += this.decimalSeparator;
39822         }
39823         if(this.allowNegative){
39824             allowed += "-";
39825         }
39826         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39827         var keyPress = function(e){
39828             var k = e.getKey();
39829             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39830                 return;
39831             }
39832             var c = e.getCharCode();
39833             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39834                 e.stopEvent();
39835             }
39836         };
39837         this.el.on("keypress", keyPress, this);
39838     },
39839
39840     // private
39841     validateValue : function(value){
39842         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39843             return false;
39844         }
39845         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39846              return true;
39847         }
39848         var num = this.parseValue(value);
39849         if(isNaN(num)){
39850             this.markInvalid(String.format(this.nanText, value));
39851             return false;
39852         }
39853         if(num < this.minValue){
39854             this.markInvalid(String.format(this.minText, this.minValue));
39855             return false;
39856         }
39857         if(num > this.maxValue){
39858             this.markInvalid(String.format(this.maxText, this.maxValue));
39859             return false;
39860         }
39861         return true;
39862     },
39863
39864     getValue : function(){
39865         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39866     },
39867
39868     // private
39869     parseValue : function(value){
39870         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39871         return isNaN(value) ? '' : value;
39872     },
39873
39874     // private
39875     fixPrecision : function(value){
39876         var nan = isNaN(value);
39877         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39878             return nan ? '' : value;
39879         }
39880         return parseFloat(value).toFixed(this.decimalPrecision);
39881     },
39882
39883     setValue : function(v){
39884         v = this.fixPrecision(v);
39885         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
39886     },
39887
39888     // private
39889     decimalPrecisionFcn : function(v){
39890         return Math.floor(v);
39891     },
39892
39893     beforeBlur : function(){
39894         var v = this.parseValue(this.getRawValue());
39895         if(v){
39896             this.setValue(v);
39897         }
39898     }
39899 });/*
39900  * Based on:
39901  * Ext JS Library 1.1.1
39902  * Copyright(c) 2006-2007, Ext JS, LLC.
39903  *
39904  * Originally Released Under LGPL - original licence link has changed is not relivant.
39905  *
39906  * Fork - LGPL
39907  * <script type="text/javascript">
39908  */
39909  
39910 /**
39911  * @class Roo.form.DateField
39912  * @extends Roo.form.TriggerField
39913  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
39914 * @constructor
39915 * Create a new DateField
39916 * @param {Object} config
39917  */
39918 Roo.form.DateField = function(config){
39919     Roo.form.DateField.superclass.constructor.call(this, config);
39920     
39921       this.addEvents({
39922          
39923         /**
39924          * @event select
39925          * Fires when a date is selected
39926              * @param {Roo.form.DateField} combo This combo box
39927              * @param {Date} date The date selected
39928              */
39929         'select' : true
39930          
39931     });
39932     
39933     
39934     if(typeof this.minValue == "string") {
39935         this.minValue = this.parseDate(this.minValue);
39936     }
39937     if(typeof this.maxValue == "string") {
39938         this.maxValue = this.parseDate(this.maxValue);
39939     }
39940     this.ddMatch = null;
39941     if(this.disabledDates){
39942         var dd = this.disabledDates;
39943         var re = "(?:";
39944         for(var i = 0; i < dd.length; i++){
39945             re += dd[i];
39946             if(i != dd.length-1) {
39947                 re += "|";
39948             }
39949         }
39950         this.ddMatch = new RegExp(re + ")");
39951     }
39952 };
39953
39954 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
39955     /**
39956      * @cfg {String} format
39957      * The default date format string which can be overriden for localization support.  The format must be
39958      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
39959      */
39960     format : "m/d/y",
39961     /**
39962      * @cfg {String} altFormats
39963      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
39964      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
39965      */
39966     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
39967     /**
39968      * @cfg {Array} disabledDays
39969      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
39970      */
39971     disabledDays : null,
39972     /**
39973      * @cfg {String} disabledDaysText
39974      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
39975      */
39976     disabledDaysText : "Disabled",
39977     /**
39978      * @cfg {Array} disabledDates
39979      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
39980      * expression so they are very powerful. Some examples:
39981      * <ul>
39982      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
39983      * <li>["03/08", "09/16"] would disable those days for every year</li>
39984      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
39985      * <li>["03/../2006"] would disable every day in March 2006</li>
39986      * <li>["^03"] would disable every day in every March</li>
39987      * </ul>
39988      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
39989      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
39990      */
39991     disabledDates : null,
39992     /**
39993      * @cfg {String} disabledDatesText
39994      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
39995      */
39996     disabledDatesText : "Disabled",
39997     /**
39998      * @cfg {Date/String} minValue
39999      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40000      * valid format (defaults to null).
40001      */
40002     minValue : null,
40003     /**
40004      * @cfg {Date/String} maxValue
40005      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40006      * valid format (defaults to null).
40007      */
40008     maxValue : null,
40009     /**
40010      * @cfg {String} minText
40011      * The error text to display when the date in the cell is before minValue (defaults to
40012      * 'The date in this field must be after {minValue}').
40013      */
40014     minText : "The date in this field must be equal to or after {0}",
40015     /**
40016      * @cfg {String} maxText
40017      * The error text to display when the date in the cell is after maxValue (defaults to
40018      * 'The date in this field must be before {maxValue}').
40019      */
40020     maxText : "The date in this field must be equal to or before {0}",
40021     /**
40022      * @cfg {String} invalidText
40023      * The error text to display when the date in the field is invalid (defaults to
40024      * '{value} is not a valid date - it must be in the format {format}').
40025      */
40026     invalidText : "{0} is not a valid date - it must be in the format {1}",
40027     /**
40028      * @cfg {String} triggerClass
40029      * An additional CSS class used to style the trigger button.  The trigger will always get the
40030      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40031      * which displays a calendar icon).
40032      */
40033     triggerClass : 'x-form-date-trigger',
40034     
40035
40036     /**
40037      * @cfg {Boolean} useIso
40038      * if enabled, then the date field will use a hidden field to store the 
40039      * real value as iso formated date. default (false)
40040      */ 
40041     useIso : false,
40042     /**
40043      * @cfg {String/Object} autoCreate
40044      * A DomHelper element spec, or true for a default element spec (defaults to
40045      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40046      */ 
40047     // private
40048     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40049     
40050     // private
40051     hiddenField: false,
40052     
40053     onRender : function(ct, position)
40054     {
40055         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40056         if (this.useIso) {
40057             //this.el.dom.removeAttribute('name'); 
40058             Roo.log("Changing name?");
40059             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40060             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40061                     'before', true);
40062             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40063             // prevent input submission
40064             this.hiddenName = this.name;
40065         }
40066             
40067             
40068     },
40069     
40070     // private
40071     validateValue : function(value)
40072     {
40073         value = this.formatDate(value);
40074         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40075             Roo.log('super failed');
40076             return false;
40077         }
40078         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40079              return true;
40080         }
40081         var svalue = value;
40082         value = this.parseDate(value);
40083         if(!value){
40084             Roo.log('parse date failed' + svalue);
40085             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40086             return false;
40087         }
40088         var time = value.getTime();
40089         if(this.minValue && time < this.minValue.getTime()){
40090             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40091             return false;
40092         }
40093         if(this.maxValue && time > this.maxValue.getTime()){
40094             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40095             return false;
40096         }
40097         if(this.disabledDays){
40098             var day = value.getDay();
40099             for(var i = 0; i < this.disabledDays.length; i++) {
40100                 if(day === this.disabledDays[i]){
40101                     this.markInvalid(this.disabledDaysText);
40102                     return false;
40103                 }
40104             }
40105         }
40106         var fvalue = this.formatDate(value);
40107         if(this.ddMatch && this.ddMatch.test(fvalue)){
40108             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40109             return false;
40110         }
40111         return true;
40112     },
40113
40114     // private
40115     // Provides logic to override the default TriggerField.validateBlur which just returns true
40116     validateBlur : function(){
40117         return !this.menu || !this.menu.isVisible();
40118     },
40119     
40120     getName: function()
40121     {
40122         // returns hidden if it's set..
40123         if (!this.rendered) {return ''};
40124         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40125         
40126     },
40127
40128     /**
40129      * Returns the current date value of the date field.
40130      * @return {Date} The date value
40131      */
40132     getValue : function(){
40133         
40134         return  this.hiddenField ?
40135                 this.hiddenField.value :
40136                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40137     },
40138
40139     /**
40140      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40141      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40142      * (the default format used is "m/d/y").
40143      * <br />Usage:
40144      * <pre><code>
40145 //All of these calls set the same date value (May 4, 2006)
40146
40147 //Pass a date object:
40148 var dt = new Date('5/4/06');
40149 dateField.setValue(dt);
40150
40151 //Pass a date string (default format):
40152 dateField.setValue('5/4/06');
40153
40154 //Pass a date string (custom format):
40155 dateField.format = 'Y-m-d';
40156 dateField.setValue('2006-5-4');
40157 </code></pre>
40158      * @param {String/Date} date The date or valid date string
40159      */
40160     setValue : function(date){
40161         if (this.hiddenField) {
40162             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40163         }
40164         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40165         // make sure the value field is always stored as a date..
40166         this.value = this.parseDate(date);
40167         
40168         
40169     },
40170
40171     // private
40172     parseDate : function(value){
40173         if(!value || value instanceof Date){
40174             return value;
40175         }
40176         var v = Date.parseDate(value, this.format);
40177          if (!v && this.useIso) {
40178             v = Date.parseDate(value, 'Y-m-d');
40179         }
40180         if(!v && this.altFormats){
40181             if(!this.altFormatsArray){
40182                 this.altFormatsArray = this.altFormats.split("|");
40183             }
40184             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40185                 v = Date.parseDate(value, this.altFormatsArray[i]);
40186             }
40187         }
40188         return v;
40189     },
40190
40191     // private
40192     formatDate : function(date, fmt){
40193         return (!date || !(date instanceof Date)) ?
40194                date : date.dateFormat(fmt || this.format);
40195     },
40196
40197     // private
40198     menuListeners : {
40199         select: function(m, d){
40200             
40201             this.setValue(d);
40202             this.fireEvent('select', this, d);
40203         },
40204         show : function(){ // retain focus styling
40205             this.onFocus();
40206         },
40207         hide : function(){
40208             this.focus.defer(10, this);
40209             var ml = this.menuListeners;
40210             this.menu.un("select", ml.select,  this);
40211             this.menu.un("show", ml.show,  this);
40212             this.menu.un("hide", ml.hide,  this);
40213         }
40214     },
40215
40216     // private
40217     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40218     onTriggerClick : function(){
40219         if(this.disabled){
40220             return;
40221         }
40222         if(this.menu == null){
40223             this.menu = new Roo.menu.DateMenu();
40224         }
40225         Roo.apply(this.menu.picker,  {
40226             showClear: this.allowBlank,
40227             minDate : this.minValue,
40228             maxDate : this.maxValue,
40229             disabledDatesRE : this.ddMatch,
40230             disabledDatesText : this.disabledDatesText,
40231             disabledDays : this.disabledDays,
40232             disabledDaysText : this.disabledDaysText,
40233             format : this.useIso ? 'Y-m-d' : this.format,
40234             minText : String.format(this.minText, this.formatDate(this.minValue)),
40235             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40236         });
40237         this.menu.on(Roo.apply({}, this.menuListeners, {
40238             scope:this
40239         }));
40240         this.menu.picker.setValue(this.getValue() || new Date());
40241         this.menu.show(this.el, "tl-bl?");
40242     },
40243
40244     beforeBlur : function(){
40245         var v = this.parseDate(this.getRawValue());
40246         if(v){
40247             this.setValue(v);
40248         }
40249     },
40250
40251     /*@
40252      * overide
40253      * 
40254      */
40255     isDirty : function() {
40256         if(this.disabled) {
40257             return false;
40258         }
40259         
40260         if(typeof(this.startValue) === 'undefined'){
40261             return false;
40262         }
40263         
40264         return String(this.getValue()) !== String(this.startValue);
40265         
40266     }
40267 });/*
40268  * Based on:
40269  * Ext JS Library 1.1.1
40270  * Copyright(c) 2006-2007, Ext JS, LLC.
40271  *
40272  * Originally Released Under LGPL - original licence link has changed is not relivant.
40273  *
40274  * Fork - LGPL
40275  * <script type="text/javascript">
40276  */
40277  
40278 /**
40279  * @class Roo.form.MonthField
40280  * @extends Roo.form.TriggerField
40281  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40282 * @constructor
40283 * Create a new MonthField
40284 * @param {Object} config
40285  */
40286 Roo.form.MonthField = function(config){
40287     
40288     Roo.form.MonthField.superclass.constructor.call(this, config);
40289     
40290       this.addEvents({
40291          
40292         /**
40293          * @event select
40294          * Fires when a date is selected
40295              * @param {Roo.form.MonthFieeld} combo This combo box
40296              * @param {Date} date The date selected
40297              */
40298         'select' : true
40299          
40300     });
40301     
40302     
40303     if(typeof this.minValue == "string") {
40304         this.minValue = this.parseDate(this.minValue);
40305     }
40306     if(typeof this.maxValue == "string") {
40307         this.maxValue = this.parseDate(this.maxValue);
40308     }
40309     this.ddMatch = null;
40310     if(this.disabledDates){
40311         var dd = this.disabledDates;
40312         var re = "(?:";
40313         for(var i = 0; i < dd.length; i++){
40314             re += dd[i];
40315             if(i != dd.length-1) {
40316                 re += "|";
40317             }
40318         }
40319         this.ddMatch = new RegExp(re + ")");
40320     }
40321 };
40322
40323 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40324     /**
40325      * @cfg {String} format
40326      * The default date format string which can be overriden for localization support.  The format must be
40327      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40328      */
40329     format : "M Y",
40330     /**
40331      * @cfg {String} altFormats
40332      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40333      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40334      */
40335     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40336     /**
40337      * @cfg {Array} disabledDays
40338      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40339      */
40340     disabledDays : [0,1,2,3,4,5,6],
40341     /**
40342      * @cfg {String} disabledDaysText
40343      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40344      */
40345     disabledDaysText : "Disabled",
40346     /**
40347      * @cfg {Array} disabledDates
40348      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40349      * expression so they are very powerful. Some examples:
40350      * <ul>
40351      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40352      * <li>["03/08", "09/16"] would disable those days for every year</li>
40353      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40354      * <li>["03/../2006"] would disable every day in March 2006</li>
40355      * <li>["^03"] would disable every day in every March</li>
40356      * </ul>
40357      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40358      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40359      */
40360     disabledDates : null,
40361     /**
40362      * @cfg {String} disabledDatesText
40363      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40364      */
40365     disabledDatesText : "Disabled",
40366     /**
40367      * @cfg {Date/String} minValue
40368      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40369      * valid format (defaults to null).
40370      */
40371     minValue : null,
40372     /**
40373      * @cfg {Date/String} maxValue
40374      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40375      * valid format (defaults to null).
40376      */
40377     maxValue : null,
40378     /**
40379      * @cfg {String} minText
40380      * The error text to display when the date in the cell is before minValue (defaults to
40381      * 'The date in this field must be after {minValue}').
40382      */
40383     minText : "The date in this field must be equal to or after {0}",
40384     /**
40385      * @cfg {String} maxTextf
40386      * The error text to display when the date in the cell is after maxValue (defaults to
40387      * 'The date in this field must be before {maxValue}').
40388      */
40389     maxText : "The date in this field must be equal to or before {0}",
40390     /**
40391      * @cfg {String} invalidText
40392      * The error text to display when the date in the field is invalid (defaults to
40393      * '{value} is not a valid date - it must be in the format {format}').
40394      */
40395     invalidText : "{0} is not a valid date - it must be in the format {1}",
40396     /**
40397      * @cfg {String} triggerClass
40398      * An additional CSS class used to style the trigger button.  The trigger will always get the
40399      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40400      * which displays a calendar icon).
40401      */
40402     triggerClass : 'x-form-date-trigger',
40403     
40404
40405     /**
40406      * @cfg {Boolean} useIso
40407      * if enabled, then the date field will use a hidden field to store the 
40408      * real value as iso formated date. default (true)
40409      */ 
40410     useIso : true,
40411     /**
40412      * @cfg {String/Object} autoCreate
40413      * A DomHelper element spec, or true for a default element spec (defaults to
40414      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40415      */ 
40416     // private
40417     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40418     
40419     // private
40420     hiddenField: false,
40421     
40422     hideMonthPicker : false,
40423     
40424     onRender : function(ct, position)
40425     {
40426         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40427         if (this.useIso) {
40428             this.el.dom.removeAttribute('name'); 
40429             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40430                     'before', true);
40431             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40432             // prevent input submission
40433             this.hiddenName = this.name;
40434         }
40435             
40436             
40437     },
40438     
40439     // private
40440     validateValue : function(value)
40441     {
40442         value = this.formatDate(value);
40443         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40444             return false;
40445         }
40446         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40447              return true;
40448         }
40449         var svalue = value;
40450         value = this.parseDate(value);
40451         if(!value){
40452             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40453             return false;
40454         }
40455         var time = value.getTime();
40456         if(this.minValue && time < this.minValue.getTime()){
40457             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40458             return false;
40459         }
40460         if(this.maxValue && time > this.maxValue.getTime()){
40461             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40462             return false;
40463         }
40464         /*if(this.disabledDays){
40465             var day = value.getDay();
40466             for(var i = 0; i < this.disabledDays.length; i++) {
40467                 if(day === this.disabledDays[i]){
40468                     this.markInvalid(this.disabledDaysText);
40469                     return false;
40470                 }
40471             }
40472         }
40473         */
40474         var fvalue = this.formatDate(value);
40475         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40476             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40477             return false;
40478         }
40479         */
40480         return true;
40481     },
40482
40483     // private
40484     // Provides logic to override the default TriggerField.validateBlur which just returns true
40485     validateBlur : function(){
40486         return !this.menu || !this.menu.isVisible();
40487     },
40488
40489     /**
40490      * Returns the current date value of the date field.
40491      * @return {Date} The date value
40492      */
40493     getValue : function(){
40494         
40495         
40496         
40497         return  this.hiddenField ?
40498                 this.hiddenField.value :
40499                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40500     },
40501
40502     /**
40503      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40504      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40505      * (the default format used is "m/d/y").
40506      * <br />Usage:
40507      * <pre><code>
40508 //All of these calls set the same date value (May 4, 2006)
40509
40510 //Pass a date object:
40511 var dt = new Date('5/4/06');
40512 monthField.setValue(dt);
40513
40514 //Pass a date string (default format):
40515 monthField.setValue('5/4/06');
40516
40517 //Pass a date string (custom format):
40518 monthField.format = 'Y-m-d';
40519 monthField.setValue('2006-5-4');
40520 </code></pre>
40521      * @param {String/Date} date The date or valid date string
40522      */
40523     setValue : function(date){
40524         Roo.log('month setValue' + date);
40525         // can only be first of month..
40526         
40527         var val = this.parseDate(date);
40528         
40529         if (this.hiddenField) {
40530             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40531         }
40532         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40533         this.value = this.parseDate(date);
40534     },
40535
40536     // private
40537     parseDate : function(value){
40538         if(!value || value instanceof Date){
40539             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40540             return value;
40541         }
40542         var v = Date.parseDate(value, this.format);
40543         if (!v && this.useIso) {
40544             v = Date.parseDate(value, 'Y-m-d');
40545         }
40546         if (v) {
40547             // 
40548             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40549         }
40550         
40551         
40552         if(!v && this.altFormats){
40553             if(!this.altFormatsArray){
40554                 this.altFormatsArray = this.altFormats.split("|");
40555             }
40556             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40557                 v = Date.parseDate(value, this.altFormatsArray[i]);
40558             }
40559         }
40560         return v;
40561     },
40562
40563     // private
40564     formatDate : function(date, fmt){
40565         return (!date || !(date instanceof Date)) ?
40566                date : date.dateFormat(fmt || this.format);
40567     },
40568
40569     // private
40570     menuListeners : {
40571         select: function(m, d){
40572             this.setValue(d);
40573             this.fireEvent('select', this, d);
40574         },
40575         show : function(){ // retain focus styling
40576             this.onFocus();
40577         },
40578         hide : function(){
40579             this.focus.defer(10, this);
40580             var ml = this.menuListeners;
40581             this.menu.un("select", ml.select,  this);
40582             this.menu.un("show", ml.show,  this);
40583             this.menu.un("hide", ml.hide,  this);
40584         }
40585     },
40586     // private
40587     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40588     onTriggerClick : function(){
40589         if(this.disabled){
40590             return;
40591         }
40592         if(this.menu == null){
40593             this.menu = new Roo.menu.DateMenu();
40594            
40595         }
40596         
40597         Roo.apply(this.menu.picker,  {
40598             
40599             showClear: this.allowBlank,
40600             minDate : this.minValue,
40601             maxDate : this.maxValue,
40602             disabledDatesRE : this.ddMatch,
40603             disabledDatesText : this.disabledDatesText,
40604             
40605             format : this.useIso ? 'Y-m-d' : this.format,
40606             minText : String.format(this.minText, this.formatDate(this.minValue)),
40607             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40608             
40609         });
40610          this.menu.on(Roo.apply({}, this.menuListeners, {
40611             scope:this
40612         }));
40613        
40614         
40615         var m = this.menu;
40616         var p = m.picker;
40617         
40618         // hide month picker get's called when we called by 'before hide';
40619         
40620         var ignorehide = true;
40621         p.hideMonthPicker  = function(disableAnim){
40622             if (ignorehide) {
40623                 return;
40624             }
40625              if(this.monthPicker){
40626                 Roo.log("hideMonthPicker called");
40627                 if(disableAnim === true){
40628                     this.monthPicker.hide();
40629                 }else{
40630                     this.monthPicker.slideOut('t', {duration:.2});
40631                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40632                     p.fireEvent("select", this, this.value);
40633                     m.hide();
40634                 }
40635             }
40636         }
40637         
40638         Roo.log('picker set value');
40639         Roo.log(this.getValue());
40640         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40641         m.show(this.el, 'tl-bl?');
40642         ignorehide  = false;
40643         // this will trigger hideMonthPicker..
40644         
40645         
40646         // hidden the day picker
40647         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40648         
40649         
40650         
40651       
40652         
40653         p.showMonthPicker.defer(100, p);
40654     
40655         
40656        
40657     },
40658
40659     beforeBlur : function(){
40660         var v = this.parseDate(this.getRawValue());
40661         if(v){
40662             this.setValue(v);
40663         }
40664     }
40665
40666     /** @cfg {Boolean} grow @hide */
40667     /** @cfg {Number} growMin @hide */
40668     /** @cfg {Number} growMax @hide */
40669     /**
40670      * @hide
40671      * @method autoSize
40672      */
40673 });/*
40674  * Based on:
40675  * Ext JS Library 1.1.1
40676  * Copyright(c) 2006-2007, Ext JS, LLC.
40677  *
40678  * Originally Released Under LGPL - original licence link has changed is not relivant.
40679  *
40680  * Fork - LGPL
40681  * <script type="text/javascript">
40682  */
40683  
40684
40685 /**
40686  * @class Roo.form.ComboBox
40687  * @extends Roo.form.TriggerField
40688  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40689  * @constructor
40690  * Create a new ComboBox.
40691  * @param {Object} config Configuration options
40692  */
40693 Roo.form.ComboBox = function(config){
40694     Roo.form.ComboBox.superclass.constructor.call(this, config);
40695     this.addEvents({
40696         /**
40697          * @event expand
40698          * Fires when the dropdown list is expanded
40699              * @param {Roo.form.ComboBox} combo This combo box
40700              */
40701         'expand' : true,
40702         /**
40703          * @event collapse
40704          * Fires when the dropdown list is collapsed
40705              * @param {Roo.form.ComboBox} combo This combo box
40706              */
40707         'collapse' : true,
40708         /**
40709          * @event beforeselect
40710          * Fires before a list item is selected. Return false to cancel the selection.
40711              * @param {Roo.form.ComboBox} combo This combo box
40712              * @param {Roo.data.Record} record The data record returned from the underlying store
40713              * @param {Number} index The index of the selected item in the dropdown list
40714              */
40715         'beforeselect' : true,
40716         /**
40717          * @event select
40718          * Fires when a list item is selected
40719              * @param {Roo.form.ComboBox} combo This combo box
40720              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40721              * @param {Number} index The index of the selected item in the dropdown list
40722              */
40723         'select' : true,
40724         /**
40725          * @event beforequery
40726          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40727          * The event object passed has these properties:
40728              * @param {Roo.form.ComboBox} combo This combo box
40729              * @param {String} query The query
40730              * @param {Boolean} forceAll true to force "all" query
40731              * @param {Boolean} cancel true to cancel the query
40732              * @param {Object} e The query event object
40733              */
40734         'beforequery': true,
40735          /**
40736          * @event add
40737          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40738              * @param {Roo.form.ComboBox} combo This combo box
40739              */
40740         'add' : true,
40741         /**
40742          * @event edit
40743          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40744              * @param {Roo.form.ComboBox} combo This combo box
40745              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40746              */
40747         'edit' : true
40748         
40749         
40750     });
40751     if(this.transform){
40752         this.allowDomMove = false;
40753         var s = Roo.getDom(this.transform);
40754         if(!this.hiddenName){
40755             this.hiddenName = s.name;
40756         }
40757         if(!this.store){
40758             this.mode = 'local';
40759             var d = [], opts = s.options;
40760             for(var i = 0, len = opts.length;i < len; i++){
40761                 var o = opts[i];
40762                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40763                 if(o.selected) {
40764                     this.value = value;
40765                 }
40766                 d.push([value, o.text]);
40767             }
40768             this.store = new Roo.data.SimpleStore({
40769                 'id': 0,
40770                 fields: ['value', 'text'],
40771                 data : d
40772             });
40773             this.valueField = 'value';
40774             this.displayField = 'text';
40775         }
40776         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40777         if(!this.lazyRender){
40778             this.target = true;
40779             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40780             s.parentNode.removeChild(s); // remove it
40781             this.render(this.el.parentNode);
40782         }else{
40783             s.parentNode.removeChild(s); // remove it
40784         }
40785
40786     }
40787     if (this.store) {
40788         this.store = Roo.factory(this.store, Roo.data);
40789     }
40790     
40791     this.selectedIndex = -1;
40792     if(this.mode == 'local'){
40793         if(config.queryDelay === undefined){
40794             this.queryDelay = 10;
40795         }
40796         if(config.minChars === undefined){
40797             this.minChars = 0;
40798         }
40799     }
40800 };
40801
40802 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40803     /**
40804      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40805      */
40806     /**
40807      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40808      * rendering into an Roo.Editor, defaults to false)
40809      */
40810     /**
40811      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40812      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40813      */
40814     /**
40815      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40816      */
40817     /**
40818      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40819      * the dropdown list (defaults to undefined, with no header element)
40820      */
40821
40822      /**
40823      * @cfg {String/Roo.Template} tpl The template to use to render the output
40824      */
40825      
40826     // private
40827     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40828     /**
40829      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40830      */
40831     listWidth: undefined,
40832     /**
40833      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40834      * mode = 'remote' or 'text' if mode = 'local')
40835      */
40836     displayField: undefined,
40837     /**
40838      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40839      * mode = 'remote' or 'value' if mode = 'local'). 
40840      * Note: use of a valueField requires the user make a selection
40841      * in order for a value to be mapped.
40842      */
40843     valueField: undefined,
40844     
40845     
40846     /**
40847      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40848      * field's data value (defaults to the underlying DOM element's name)
40849      */
40850     hiddenName: undefined,
40851     /**
40852      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40853      */
40854     listClass: '',
40855     /**
40856      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40857      */
40858     selectedClass: 'x-combo-selected',
40859     /**
40860      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40861      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40862      * which displays a downward arrow icon).
40863      */
40864     triggerClass : 'x-form-arrow-trigger',
40865     /**
40866      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40867      */
40868     shadow:'sides',
40869     /**
40870      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
40871      * anchor positions (defaults to 'tl-bl')
40872      */
40873     listAlign: 'tl-bl?',
40874     /**
40875      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
40876      */
40877     maxHeight: 300,
40878     /**
40879      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
40880      * query specified by the allQuery config option (defaults to 'query')
40881      */
40882     triggerAction: 'query',
40883     /**
40884      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
40885      * (defaults to 4, does not apply if editable = false)
40886      */
40887     minChars : 4,
40888     /**
40889      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
40890      * delay (typeAheadDelay) if it matches a known value (defaults to false)
40891      */
40892     typeAhead: false,
40893     /**
40894      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
40895      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
40896      */
40897     queryDelay: 500,
40898     /**
40899      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
40900      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
40901      */
40902     pageSize: 0,
40903     /**
40904      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
40905      * when editable = true (defaults to false)
40906      */
40907     selectOnFocus:false,
40908     /**
40909      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
40910      */
40911     queryParam: 'query',
40912     /**
40913      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
40914      * when mode = 'remote' (defaults to 'Loading...')
40915      */
40916     loadingText: 'Loading...',
40917     /**
40918      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
40919      */
40920     resizable: false,
40921     /**
40922      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
40923      */
40924     handleHeight : 8,
40925     /**
40926      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
40927      * traditional select (defaults to true)
40928      */
40929     editable: true,
40930     /**
40931      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
40932      */
40933     allQuery: '',
40934     /**
40935      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
40936      */
40937     mode: 'remote',
40938     /**
40939      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
40940      * listWidth has a higher value)
40941      */
40942     minListWidth : 70,
40943     /**
40944      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
40945      * allow the user to set arbitrary text into the field (defaults to false)
40946      */
40947     forceSelection:false,
40948     /**
40949      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
40950      * if typeAhead = true (defaults to 250)
40951      */
40952     typeAheadDelay : 250,
40953     /**
40954      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
40955      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
40956      */
40957     valueNotFoundText : undefined,
40958     /**
40959      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
40960      */
40961     blockFocus : false,
40962     
40963     /**
40964      * @cfg {Boolean} disableClear Disable showing of clear button.
40965      */
40966     disableClear : false,
40967     /**
40968      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
40969      */
40970     alwaysQuery : false,
40971     
40972     //private
40973     addicon : false,
40974     editicon: false,
40975     
40976     // element that contains real text value.. (when hidden is used..)
40977      
40978     // private
40979     onRender : function(ct, position){
40980         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
40981         if(this.hiddenName){
40982             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
40983                     'before', true);
40984             this.hiddenField.value =
40985                 this.hiddenValue !== undefined ? this.hiddenValue :
40986                 this.value !== undefined ? this.value : '';
40987
40988             // prevent input submission
40989             this.el.dom.removeAttribute('name');
40990              
40991              
40992         }
40993         if(Roo.isGecko){
40994             this.el.dom.setAttribute('autocomplete', 'off');
40995         }
40996
40997         var cls = 'x-combo-list';
40998
40999         this.list = new Roo.Layer({
41000             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41001         });
41002
41003         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41004         this.list.setWidth(lw);
41005         this.list.swallowEvent('mousewheel');
41006         this.assetHeight = 0;
41007
41008         if(this.title){
41009             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41010             this.assetHeight += this.header.getHeight();
41011         }
41012
41013         this.innerList = this.list.createChild({cls:cls+'-inner'});
41014         this.innerList.on('mouseover', this.onViewOver, this);
41015         this.innerList.on('mousemove', this.onViewMove, this);
41016         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41017         
41018         if(this.allowBlank && !this.pageSize && !this.disableClear){
41019             this.footer = this.list.createChild({cls:cls+'-ft'});
41020             this.pageTb = new Roo.Toolbar(this.footer);
41021            
41022         }
41023         if(this.pageSize){
41024             this.footer = this.list.createChild({cls:cls+'-ft'});
41025             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41026                     {pageSize: this.pageSize});
41027             
41028         }
41029         
41030         if (this.pageTb && this.allowBlank && !this.disableClear) {
41031             var _this = this;
41032             this.pageTb.add(new Roo.Toolbar.Fill(), {
41033                 cls: 'x-btn-icon x-btn-clear',
41034                 text: '&#160;',
41035                 handler: function()
41036                 {
41037                     _this.collapse();
41038                     _this.clearValue();
41039                     _this.onSelect(false, -1);
41040                 }
41041             });
41042         }
41043         if (this.footer) {
41044             this.assetHeight += this.footer.getHeight();
41045         }
41046         
41047
41048         if(!this.tpl){
41049             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41050         }
41051
41052         this.view = new Roo.View(this.innerList, this.tpl, {
41053             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41054         });
41055
41056         this.view.on('click', this.onViewClick, this);
41057
41058         this.store.on('beforeload', this.onBeforeLoad, this);
41059         this.store.on('load', this.onLoad, this);
41060         this.store.on('loadexception', this.onLoadException, this);
41061
41062         if(this.resizable){
41063             this.resizer = new Roo.Resizable(this.list,  {
41064                pinned:true, handles:'se'
41065             });
41066             this.resizer.on('resize', function(r, w, h){
41067                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41068                 this.listWidth = w;
41069                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41070                 this.restrictHeight();
41071             }, this);
41072             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41073         }
41074         if(!this.editable){
41075             this.editable = true;
41076             this.setEditable(false);
41077         }  
41078         
41079         
41080         if (typeof(this.events.add.listeners) != 'undefined') {
41081             
41082             this.addicon = this.wrap.createChild(
41083                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41084        
41085             this.addicon.on('click', function(e) {
41086                 this.fireEvent('add', this);
41087             }, this);
41088         }
41089         if (typeof(this.events.edit.listeners) != 'undefined') {
41090             
41091             this.editicon = this.wrap.createChild(
41092                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41093             if (this.addicon) {
41094                 this.editicon.setStyle('margin-left', '40px');
41095             }
41096             this.editicon.on('click', function(e) {
41097                 
41098                 // we fire even  if inothing is selected..
41099                 this.fireEvent('edit', this, this.lastData );
41100                 
41101             }, this);
41102         }
41103         
41104         
41105         
41106     },
41107
41108     // private
41109     initEvents : function(){
41110         Roo.form.ComboBox.superclass.initEvents.call(this);
41111
41112         this.keyNav = new Roo.KeyNav(this.el, {
41113             "up" : function(e){
41114                 this.inKeyMode = true;
41115                 this.selectPrev();
41116             },
41117
41118             "down" : function(e){
41119                 if(!this.isExpanded()){
41120                     this.onTriggerClick();
41121                 }else{
41122                     this.inKeyMode = true;
41123                     this.selectNext();
41124                 }
41125             },
41126
41127             "enter" : function(e){
41128                 this.onViewClick();
41129                 //return true;
41130             },
41131
41132             "esc" : function(e){
41133                 this.collapse();
41134             },
41135
41136             "tab" : function(e){
41137                 this.onViewClick(false);
41138                 this.fireEvent("specialkey", this, e);
41139                 return true;
41140             },
41141
41142             scope : this,
41143
41144             doRelay : function(foo, bar, hname){
41145                 if(hname == 'down' || this.scope.isExpanded()){
41146                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41147                 }
41148                 return true;
41149             },
41150
41151             forceKeyDown: true
41152         });
41153         this.queryDelay = Math.max(this.queryDelay || 10,
41154                 this.mode == 'local' ? 10 : 250);
41155         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41156         if(this.typeAhead){
41157             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41158         }
41159         if(this.editable !== false){
41160             this.el.on("keyup", this.onKeyUp, this);
41161         }
41162         if(this.forceSelection){
41163             this.on('blur', this.doForce, this);
41164         }
41165     },
41166
41167     onDestroy : function(){
41168         if(this.view){
41169             this.view.setStore(null);
41170             this.view.el.removeAllListeners();
41171             this.view.el.remove();
41172             this.view.purgeListeners();
41173         }
41174         if(this.list){
41175             this.list.destroy();
41176         }
41177         if(this.store){
41178             this.store.un('beforeload', this.onBeforeLoad, this);
41179             this.store.un('load', this.onLoad, this);
41180             this.store.un('loadexception', this.onLoadException, this);
41181         }
41182         Roo.form.ComboBox.superclass.onDestroy.call(this);
41183     },
41184
41185     // private
41186     fireKey : function(e){
41187         if(e.isNavKeyPress() && !this.list.isVisible()){
41188             this.fireEvent("specialkey", this, e);
41189         }
41190     },
41191
41192     // private
41193     onResize: function(w, h){
41194         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41195         
41196         if(typeof w != 'number'){
41197             // we do not handle it!?!?
41198             return;
41199         }
41200         var tw = this.trigger.getWidth();
41201         tw += this.addicon ? this.addicon.getWidth() : 0;
41202         tw += this.editicon ? this.editicon.getWidth() : 0;
41203         var x = w - tw;
41204         this.el.setWidth( this.adjustWidth('input', x));
41205             
41206         this.trigger.setStyle('left', x+'px');
41207         
41208         if(this.list && this.listWidth === undefined){
41209             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41210             this.list.setWidth(lw);
41211             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41212         }
41213         
41214     
41215         
41216     },
41217
41218     /**
41219      * Allow or prevent the user from directly editing the field text.  If false is passed,
41220      * the user will only be able to select from the items defined in the dropdown list.  This method
41221      * is the runtime equivalent of setting the 'editable' config option at config time.
41222      * @param {Boolean} value True to allow the user to directly edit the field text
41223      */
41224     setEditable : function(value){
41225         if(value == this.editable){
41226             return;
41227         }
41228         this.editable = value;
41229         if(!value){
41230             this.el.dom.setAttribute('readOnly', true);
41231             this.el.on('mousedown', this.onTriggerClick,  this);
41232             this.el.addClass('x-combo-noedit');
41233         }else{
41234             this.el.dom.setAttribute('readOnly', false);
41235             this.el.un('mousedown', this.onTriggerClick,  this);
41236             this.el.removeClass('x-combo-noedit');
41237         }
41238     },
41239
41240     // private
41241     onBeforeLoad : function(){
41242         if(!this.hasFocus){
41243             return;
41244         }
41245         this.innerList.update(this.loadingText ?
41246                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41247         this.restrictHeight();
41248         this.selectedIndex = -1;
41249     },
41250
41251     // private
41252     onLoad : function(){
41253         if(!this.hasFocus){
41254             return;
41255         }
41256         if(this.store.getCount() > 0){
41257             this.expand();
41258             this.restrictHeight();
41259             if(this.lastQuery == this.allQuery){
41260                 if(this.editable){
41261                     this.el.dom.select();
41262                 }
41263                 if(!this.selectByValue(this.value, true)){
41264                     this.select(0, true);
41265                 }
41266             }else{
41267                 this.selectNext();
41268                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41269                     this.taTask.delay(this.typeAheadDelay);
41270                 }
41271             }
41272         }else{
41273             this.onEmptyResults();
41274         }
41275         //this.el.focus();
41276     },
41277     // private
41278     onLoadException : function()
41279     {
41280         this.collapse();
41281         Roo.log(this.store.reader.jsonData);
41282         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41283             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41284         }
41285         
41286         
41287     },
41288     // private
41289     onTypeAhead : function(){
41290         if(this.store.getCount() > 0){
41291             var r = this.store.getAt(0);
41292             var newValue = r.data[this.displayField];
41293             var len = newValue.length;
41294             var selStart = this.getRawValue().length;
41295             if(selStart != len){
41296                 this.setRawValue(newValue);
41297                 this.selectText(selStart, newValue.length);
41298             }
41299         }
41300     },
41301
41302     // private
41303     onSelect : function(record, index){
41304         if(this.fireEvent('beforeselect', this, record, index) !== false){
41305             this.setFromData(index > -1 ? record.data : false);
41306             this.collapse();
41307             this.fireEvent('select', this, record, index);
41308         }
41309     },
41310
41311     /**
41312      * Returns the currently selected field value or empty string if no value is set.
41313      * @return {String} value The selected value
41314      */
41315     getValue : function(){
41316         if(this.valueField){
41317             return typeof this.value != 'undefined' ? this.value : '';
41318         }
41319         return Roo.form.ComboBox.superclass.getValue.call(this);
41320     },
41321
41322     /**
41323      * Clears any text/value currently set in the field
41324      */
41325     clearValue : function(){
41326         if(this.hiddenField){
41327             this.hiddenField.value = '';
41328         }
41329         this.value = '';
41330         this.setRawValue('');
41331         this.lastSelectionText = '';
41332         
41333     },
41334
41335     /**
41336      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41337      * will be displayed in the field.  If the value does not match the data value of an existing item,
41338      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41339      * Otherwise the field will be blank (although the value will still be set).
41340      * @param {String} value The value to match
41341      */
41342     setValue : function(v){
41343         var text = v;
41344         if(this.valueField){
41345             var r = this.findRecord(this.valueField, v);
41346             if(r){
41347                 text = r.data[this.displayField];
41348             }else if(this.valueNotFoundText !== undefined){
41349                 text = this.valueNotFoundText;
41350             }
41351         }
41352         this.lastSelectionText = text;
41353         if(this.hiddenField){
41354             this.hiddenField.value = v;
41355         }
41356         Roo.form.ComboBox.superclass.setValue.call(this, text);
41357         this.value = v;
41358     },
41359     /**
41360      * @property {Object} the last set data for the element
41361      */
41362     
41363     lastData : false,
41364     /**
41365      * Sets the value of the field based on a object which is related to the record format for the store.
41366      * @param {Object} value the value to set as. or false on reset?
41367      */
41368     setFromData : function(o){
41369         var dv = ''; // display value
41370         var vv = ''; // value value..
41371         this.lastData = o;
41372         if (this.displayField) {
41373             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41374         } else {
41375             // this is an error condition!!!
41376             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41377         }
41378         
41379         if(this.valueField){
41380             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41381         }
41382         if(this.hiddenField){
41383             this.hiddenField.value = vv;
41384             
41385             this.lastSelectionText = dv;
41386             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41387             this.value = vv;
41388             return;
41389         }
41390         // no hidden field.. - we store the value in 'value', but still display
41391         // display field!!!!
41392         this.lastSelectionText = dv;
41393         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41394         this.value = vv;
41395         
41396         
41397     },
41398     // private
41399     reset : function(){
41400         // overridden so that last data is reset..
41401         this.setValue(this.resetValue);
41402         this.clearInvalid();
41403         this.lastData = false;
41404         if (this.view) {
41405             this.view.clearSelections();
41406         }
41407     },
41408     // private
41409     findRecord : function(prop, value){
41410         var record;
41411         if(this.store.getCount() > 0){
41412             this.store.each(function(r){
41413                 if(r.data[prop] == value){
41414                     record = r;
41415                     return false;
41416                 }
41417                 return true;
41418             });
41419         }
41420         return record;
41421     },
41422     
41423     getName: function()
41424     {
41425         // returns hidden if it's set..
41426         if (!this.rendered) {return ''};
41427         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41428         
41429     },
41430     // private
41431     onViewMove : function(e, t){
41432         this.inKeyMode = false;
41433     },
41434
41435     // private
41436     onViewOver : function(e, t){
41437         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41438             return;
41439         }
41440         var item = this.view.findItemFromChild(t);
41441         if(item){
41442             var index = this.view.indexOf(item);
41443             this.select(index, false);
41444         }
41445     },
41446
41447     // private
41448     onViewClick : function(doFocus)
41449     {
41450         var index = this.view.getSelectedIndexes()[0];
41451         var r = this.store.getAt(index);
41452         if(r){
41453             this.onSelect(r, index);
41454         }
41455         if(doFocus !== false && !this.blockFocus){
41456             this.el.focus();
41457         }
41458     },
41459
41460     // private
41461     restrictHeight : function(){
41462         this.innerList.dom.style.height = '';
41463         var inner = this.innerList.dom;
41464         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41465         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41466         this.list.beginUpdate();
41467         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41468         this.list.alignTo(this.el, this.listAlign);
41469         this.list.endUpdate();
41470     },
41471
41472     // private
41473     onEmptyResults : function(){
41474         this.collapse();
41475     },
41476
41477     /**
41478      * Returns true if the dropdown list is expanded, else false.
41479      */
41480     isExpanded : function(){
41481         return this.list.isVisible();
41482     },
41483
41484     /**
41485      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41486      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41487      * @param {String} value The data value of the item to select
41488      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41489      * selected item if it is not currently in view (defaults to true)
41490      * @return {Boolean} True if the value matched an item in the list, else false
41491      */
41492     selectByValue : function(v, scrollIntoView){
41493         if(v !== undefined && v !== null){
41494             var r = this.findRecord(this.valueField || this.displayField, v);
41495             if(r){
41496                 this.select(this.store.indexOf(r), scrollIntoView);
41497                 return true;
41498             }
41499         }
41500         return false;
41501     },
41502
41503     /**
41504      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41505      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41506      * @param {Number} index The zero-based index of the list item to select
41507      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41508      * selected item if it is not currently in view (defaults to true)
41509      */
41510     select : function(index, scrollIntoView){
41511         this.selectedIndex = index;
41512         this.view.select(index);
41513         if(scrollIntoView !== false){
41514             var el = this.view.getNode(index);
41515             if(el){
41516                 this.innerList.scrollChildIntoView(el, false);
41517             }
41518         }
41519     },
41520
41521     // private
41522     selectNext : function(){
41523         var ct = this.store.getCount();
41524         if(ct > 0){
41525             if(this.selectedIndex == -1){
41526                 this.select(0);
41527             }else if(this.selectedIndex < ct-1){
41528                 this.select(this.selectedIndex+1);
41529             }
41530         }
41531     },
41532
41533     // private
41534     selectPrev : function(){
41535         var ct = this.store.getCount();
41536         if(ct > 0){
41537             if(this.selectedIndex == -1){
41538                 this.select(0);
41539             }else if(this.selectedIndex != 0){
41540                 this.select(this.selectedIndex-1);
41541             }
41542         }
41543     },
41544
41545     // private
41546     onKeyUp : function(e){
41547         if(this.editable !== false && !e.isSpecialKey()){
41548             this.lastKey = e.getKey();
41549             this.dqTask.delay(this.queryDelay);
41550         }
41551     },
41552
41553     // private
41554     validateBlur : function(){
41555         return !this.list || !this.list.isVisible();   
41556     },
41557
41558     // private
41559     initQuery : function(){
41560         this.doQuery(this.getRawValue());
41561     },
41562
41563     // private
41564     doForce : function(){
41565         if(this.el.dom.value.length > 0){
41566             this.el.dom.value =
41567                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41568              
41569         }
41570     },
41571
41572     /**
41573      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41574      * query allowing the query action to be canceled if needed.
41575      * @param {String} query The SQL query to execute
41576      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41577      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41578      * saved in the current store (defaults to false)
41579      */
41580     doQuery : function(q, forceAll){
41581         if(q === undefined || q === null){
41582             q = '';
41583         }
41584         var qe = {
41585             query: q,
41586             forceAll: forceAll,
41587             combo: this,
41588             cancel:false
41589         };
41590         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41591             return false;
41592         }
41593         q = qe.query;
41594         forceAll = qe.forceAll;
41595         if(forceAll === true || (q.length >= this.minChars)){
41596             if(this.lastQuery != q || this.alwaysQuery){
41597                 this.lastQuery = q;
41598                 if(this.mode == 'local'){
41599                     this.selectedIndex = -1;
41600                     if(forceAll){
41601                         this.store.clearFilter();
41602                     }else{
41603                         this.store.filter(this.displayField, q);
41604                     }
41605                     this.onLoad();
41606                 }else{
41607                     this.store.baseParams[this.queryParam] = q;
41608                     this.store.load({
41609                         params: this.getParams(q)
41610                     });
41611                     this.expand();
41612                 }
41613             }else{
41614                 this.selectedIndex = -1;
41615                 this.onLoad();   
41616             }
41617         }
41618     },
41619
41620     // private
41621     getParams : function(q){
41622         var p = {};
41623         //p[this.queryParam] = q;
41624         if(this.pageSize){
41625             p.start = 0;
41626             p.limit = this.pageSize;
41627         }
41628         return p;
41629     },
41630
41631     /**
41632      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41633      */
41634     collapse : function(){
41635         if(!this.isExpanded()){
41636             return;
41637         }
41638         this.list.hide();
41639         Roo.get(document).un('mousedown', this.collapseIf, this);
41640         Roo.get(document).un('mousewheel', this.collapseIf, this);
41641         if (!this.editable) {
41642             Roo.get(document).un('keydown', this.listKeyPress, this);
41643         }
41644         this.fireEvent('collapse', this);
41645     },
41646
41647     // private
41648     collapseIf : function(e){
41649         if(!e.within(this.wrap) && !e.within(this.list)){
41650             this.collapse();
41651         }
41652     },
41653
41654     /**
41655      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41656      */
41657     expand : function(){
41658         if(this.isExpanded() || !this.hasFocus){
41659             return;
41660         }
41661         this.list.alignTo(this.el, this.listAlign);
41662         this.list.show();
41663         Roo.get(document).on('mousedown', this.collapseIf, this);
41664         Roo.get(document).on('mousewheel', this.collapseIf, this);
41665         if (!this.editable) {
41666             Roo.get(document).on('keydown', this.listKeyPress, this);
41667         }
41668         
41669         this.fireEvent('expand', this);
41670     },
41671
41672     // private
41673     // Implements the default empty TriggerField.onTriggerClick function
41674     onTriggerClick : function(){
41675         if(this.disabled){
41676             return;
41677         }
41678         if(this.isExpanded()){
41679             this.collapse();
41680             if (!this.blockFocus) {
41681                 this.el.focus();
41682             }
41683             
41684         }else {
41685             this.hasFocus = true;
41686             if(this.triggerAction == 'all') {
41687                 this.doQuery(this.allQuery, true);
41688             } else {
41689                 this.doQuery(this.getRawValue());
41690             }
41691             if (!this.blockFocus) {
41692                 this.el.focus();
41693             }
41694         }
41695     },
41696     listKeyPress : function(e)
41697     {
41698         //Roo.log('listkeypress');
41699         // scroll to first matching element based on key pres..
41700         if (e.isSpecialKey()) {
41701             return false;
41702         }
41703         var k = String.fromCharCode(e.getKey()).toUpperCase();
41704         //Roo.log(k);
41705         var match  = false;
41706         var csel = this.view.getSelectedNodes();
41707         var cselitem = false;
41708         if (csel.length) {
41709             var ix = this.view.indexOf(csel[0]);
41710             cselitem  = this.store.getAt(ix);
41711             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41712                 cselitem = false;
41713             }
41714             
41715         }
41716         
41717         this.store.each(function(v) { 
41718             if (cselitem) {
41719                 // start at existing selection.
41720                 if (cselitem.id == v.id) {
41721                     cselitem = false;
41722                 }
41723                 return;
41724             }
41725                 
41726             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41727                 match = this.store.indexOf(v);
41728                 return false;
41729             }
41730         }, this);
41731         
41732         if (match === false) {
41733             return true; // no more action?
41734         }
41735         // scroll to?
41736         this.view.select(match);
41737         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41738         sn.scrollIntoView(sn.dom.parentNode, false);
41739     }
41740
41741     /** 
41742     * @cfg {Boolean} grow 
41743     * @hide 
41744     */
41745     /** 
41746     * @cfg {Number} growMin 
41747     * @hide 
41748     */
41749     /** 
41750     * @cfg {Number} growMax 
41751     * @hide 
41752     */
41753     /**
41754      * @hide
41755      * @method autoSize
41756      */
41757 });/*
41758  * Copyright(c) 2010-2012, Roo J Solutions Limited
41759  *
41760  * Licence LGPL
41761  *
41762  */
41763
41764 /**
41765  * @class Roo.form.ComboBoxArray
41766  * @extends Roo.form.TextField
41767  * A facebook style adder... for lists of email / people / countries  etc...
41768  * pick multiple items from a combo box, and shows each one.
41769  *
41770  *  Fred [x]  Brian [x]  [Pick another |v]
41771  *
41772  *
41773  *  For this to work: it needs various extra information
41774  *    - normal combo problay has
41775  *      name, hiddenName
41776  *    + displayField, valueField
41777  *
41778  *    For our purpose...
41779  *
41780  *
41781  *   If we change from 'extends' to wrapping...
41782  *   
41783  *  
41784  *
41785  
41786  
41787  * @constructor
41788  * Create a new ComboBoxArray.
41789  * @param {Object} config Configuration options
41790  */
41791  
41792
41793 Roo.form.ComboBoxArray = function(config)
41794 {
41795     this.addEvents({
41796         /**
41797          * @event beforeremove
41798          * Fires before 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         'beforeremove' : true,
41803         /**
41804          * @event remove
41805          * Fires when remove the value from the list
41806              * @param {Roo.form.ComboBoxArray} _self This combo box array
41807              * @param {Roo.form.ComboBoxArray.Item} item removed item
41808              */
41809         'remove' : true
41810         
41811         
41812     });
41813     
41814     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41815     
41816     this.items = new Roo.util.MixedCollection(false);
41817     
41818     // construct the child combo...
41819     
41820     
41821     
41822     
41823    
41824     
41825 }
41826
41827  
41828 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41829
41830     /**
41831      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41832      */
41833     
41834     lastData : false,
41835     
41836     // behavies liek a hiddne field
41837     inputType:      'hidden',
41838     /**
41839      * @cfg {Number} width The width of the box that displays the selected element
41840      */ 
41841     width:          300,
41842
41843     
41844     
41845     /**
41846      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41847      */
41848     name : false,
41849     /**
41850      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41851      */
41852     hiddenName : false,
41853     
41854     
41855     // private the array of items that are displayed..
41856     items  : false,
41857     // private - the hidden field el.
41858     hiddenEl : false,
41859     // private - the filed el..
41860     el : false,
41861     
41862     //validateValue : function() { return true; }, // all values are ok!
41863     //onAddClick: function() { },
41864     
41865     onRender : function(ct, position) 
41866     {
41867         
41868         // create the standard hidden element
41869         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
41870         
41871         
41872         // give fake names to child combo;
41873         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
41874         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
41875         
41876         this.combo = Roo.factory(this.combo, Roo.form);
41877         this.combo.onRender(ct, position);
41878         if (typeof(this.combo.width) != 'undefined') {
41879             this.combo.onResize(this.combo.width,0);
41880         }
41881         
41882         this.combo.initEvents();
41883         
41884         // assigned so form know we need to do this..
41885         this.store          = this.combo.store;
41886         this.valueField     = this.combo.valueField;
41887         this.displayField   = this.combo.displayField ;
41888         
41889         
41890         this.combo.wrap.addClass('x-cbarray-grp');
41891         
41892         var cbwrap = this.combo.wrap.createChild(
41893             {tag: 'div', cls: 'x-cbarray-cb'},
41894             this.combo.el.dom
41895         );
41896         
41897              
41898         this.hiddenEl = this.combo.wrap.createChild({
41899             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
41900         });
41901         this.el = this.combo.wrap.createChild({
41902             tag: 'input',  type:'hidden' , name: this.name, value : ''
41903         });
41904          //   this.el.dom.removeAttribute("name");
41905         
41906         
41907         this.outerWrap = this.combo.wrap;
41908         this.wrap = cbwrap;
41909         
41910         this.outerWrap.setWidth(this.width);
41911         this.outerWrap.dom.removeChild(this.el.dom);
41912         
41913         this.wrap.dom.appendChild(this.el.dom);
41914         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
41915         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
41916         
41917         this.combo.trigger.setStyle('position','relative');
41918         this.combo.trigger.setStyle('left', '0px');
41919         this.combo.trigger.setStyle('top', '2px');
41920         
41921         this.combo.el.setStyle('vertical-align', 'text-bottom');
41922         
41923         //this.trigger.setStyle('vertical-align', 'top');
41924         
41925         // this should use the code from combo really... on('add' ....)
41926         if (this.adder) {
41927             
41928         
41929             this.adder = this.outerWrap.createChild(
41930                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
41931             var _t = this;
41932             this.adder.on('click', function(e) {
41933                 _t.fireEvent('adderclick', this, e);
41934             }, _t);
41935         }
41936         //var _t = this;
41937         //this.adder.on('click', this.onAddClick, _t);
41938         
41939         
41940         this.combo.on('select', function(cb, rec, ix) {
41941             this.addItem(rec.data);
41942             
41943             cb.setValue('');
41944             cb.el.dom.value = '';
41945             //cb.lastData = rec.data;
41946             // add to list
41947             
41948         }, this);
41949         
41950         
41951     },
41952     
41953     
41954     getName: function()
41955     {
41956         // returns hidden if it's set..
41957         if (!this.rendered) {return ''};
41958         return  this.hiddenName ? this.hiddenName : this.name;
41959         
41960     },
41961     
41962     
41963     onResize: function(w, h){
41964         
41965         return;
41966         // not sure if this is needed..
41967         //this.combo.onResize(w,h);
41968         
41969         if(typeof w != 'number'){
41970             // we do not handle it!?!?
41971             return;
41972         }
41973         var tw = this.combo.trigger.getWidth();
41974         tw += this.addicon ? this.addicon.getWidth() : 0;
41975         tw += this.editicon ? this.editicon.getWidth() : 0;
41976         var x = w - tw;
41977         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
41978             
41979         this.combo.trigger.setStyle('left', '0px');
41980         
41981         if(this.list && this.listWidth === undefined){
41982             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
41983             this.list.setWidth(lw);
41984             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41985         }
41986         
41987     
41988         
41989     },
41990     
41991     addItem: function(rec)
41992     {
41993         var valueField = this.combo.valueField;
41994         var displayField = this.combo.displayField;
41995         if (this.items.indexOfKey(rec[valueField]) > -1) {
41996             //console.log("GOT " + rec.data.id);
41997             return;
41998         }
41999         
42000         var x = new Roo.form.ComboBoxArray.Item({
42001             //id : rec[this.idField],
42002             data : rec,
42003             displayField : displayField ,
42004             tipField : displayField ,
42005             cb : this
42006         });
42007         // use the 
42008         this.items.add(rec[valueField],x);
42009         // add it before the element..
42010         this.updateHiddenEl();
42011         x.render(this.outerWrap, this.wrap.dom);
42012         // add the image handler..
42013     },
42014     
42015     updateHiddenEl : function()
42016     {
42017         this.validate();
42018         if (!this.hiddenEl) {
42019             return;
42020         }
42021         var ar = [];
42022         var idField = this.combo.valueField;
42023         
42024         this.items.each(function(f) {
42025             ar.push(f.data[idField]);
42026            
42027         });
42028         this.hiddenEl.dom.value = ar.join(',');
42029         this.validate();
42030     },
42031     
42032     reset : function()
42033     {
42034         this.items.clear();
42035         
42036         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42037            el.remove();
42038         });
42039         
42040         this.el.dom.value = '';
42041         if (this.hiddenEl) {
42042             this.hiddenEl.dom.value = '';
42043         }
42044         
42045     },
42046     getValue: function()
42047     {
42048         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42049     },
42050     setValue: function(v) // not a valid action - must use addItems..
42051     {
42052          
42053         this.reset();
42054         
42055         
42056         
42057         if (this.store.isLocal && (typeof(v) == 'string')) {
42058             // then we can use the store to find the values..
42059             // comma seperated at present.. this needs to allow JSON based encoding..
42060             this.hiddenEl.value  = v;
42061             var v_ar = [];
42062             Roo.each(v.split(','), function(k) {
42063                 Roo.log("CHECK " + this.valueField + ',' + k);
42064                 var li = this.store.query(this.valueField, k);
42065                 if (!li.length) {
42066                     return;
42067                 }
42068                 var add = {};
42069                 add[this.valueField] = k;
42070                 add[this.displayField] = li.item(0).data[this.displayField];
42071                 
42072                 this.addItem(add);
42073             }, this) 
42074              
42075         }
42076         if (typeof(v) == 'object' ) {
42077             // then let's assume it's an array of objects..
42078             Roo.each(v, function(l) {
42079                 this.addItem(l);
42080             }, this);
42081              
42082         }
42083         
42084         
42085     },
42086     setFromData: function(v)
42087     {
42088         // this recieves an object, if setValues is called.
42089         this.reset();
42090         this.el.dom.value = v[this.displayField];
42091         this.hiddenEl.dom.value = v[this.valueField];
42092         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42093             return;
42094         }
42095         var kv = v[this.valueField];
42096         var dv = v[this.displayField];
42097         kv = typeof(kv) != 'string' ? '' : kv;
42098         dv = typeof(dv) != 'string' ? '' : dv;
42099         
42100         
42101         var keys = kv.split(',');
42102         var display = dv.split(',');
42103         for (var i = 0 ; i < keys.length; i++) {
42104             
42105             add = {};
42106             add[this.valueField] = keys[i];
42107             add[this.displayField] = display[i];
42108             this.addItem(add);
42109         }
42110       
42111         
42112     },
42113     
42114     /**
42115      * Validates the combox array value
42116      * @return {Boolean} True if the value is valid, else false
42117      */
42118     validate : function(){
42119         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42120             this.clearInvalid();
42121             return true;
42122         }
42123         return false;
42124     },
42125     
42126     validateValue : function(value){
42127         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42128         
42129     },
42130     
42131     /*@
42132      * overide
42133      * 
42134      */
42135     isDirty : function() {
42136         if(this.disabled) {
42137             return false;
42138         }
42139         
42140         try {
42141             var d = Roo.decode(String(this.originalValue));
42142         } catch (e) {
42143             return String(this.getValue()) !== String(this.originalValue);
42144         }
42145         
42146         var originalValue = [];
42147         
42148         for (var i = 0; i < d.length; i++){
42149             originalValue.push(d[i][this.valueField]);
42150         }
42151         
42152         return String(this.getValue()) !== String(originalValue.join(','));
42153         
42154     }
42155     
42156 });
42157
42158
42159
42160 /**
42161  * @class Roo.form.ComboBoxArray.Item
42162  * @extends Roo.BoxComponent
42163  * A selected item in the list
42164  *  Fred [x]  Brian [x]  [Pick another |v]
42165  * 
42166  * @constructor
42167  * Create a new item.
42168  * @param {Object} config Configuration options
42169  */
42170  
42171 Roo.form.ComboBoxArray.Item = function(config) {
42172     config.id = Roo.id();
42173     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42174 }
42175
42176 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42177     data : {},
42178     cb: false,
42179     displayField : false,
42180     tipField : false,
42181     
42182     
42183     defaultAutoCreate : {
42184         tag: 'div',
42185         cls: 'x-cbarray-item',
42186         cn : [ 
42187             { tag: 'div' },
42188             {
42189                 tag: 'img',
42190                 width:16,
42191                 height : 16,
42192                 src : Roo.BLANK_IMAGE_URL ,
42193                 align: 'center'
42194             }
42195         ]
42196         
42197     },
42198     
42199  
42200     onRender : function(ct, position)
42201     {
42202         Roo.form.Field.superclass.onRender.call(this, ct, position);
42203         
42204         if(!this.el){
42205             var cfg = this.getAutoCreate();
42206             this.el = ct.createChild(cfg, position);
42207         }
42208         
42209         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42210         
42211         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42212             this.cb.renderer(this.data) :
42213             String.format('{0}',this.data[this.displayField]);
42214         
42215             
42216         this.el.child('div').dom.setAttribute('qtip',
42217                         String.format('{0}',this.data[this.tipField])
42218         );
42219         
42220         this.el.child('img').on('click', this.remove, this);
42221         
42222     },
42223    
42224     remove : function()
42225     {
42226         if(this.cb.disabled){
42227             return;
42228         }
42229         
42230         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42231             this.cb.items.remove(this);
42232             this.el.child('img').un('click', this.remove, this);
42233             this.el.remove();
42234             this.cb.updateHiddenEl();
42235
42236             this.cb.fireEvent('remove', this.cb, this);
42237         }
42238         
42239     }
42240 });/*
42241  * Based on:
42242  * Ext JS Library 1.1.1
42243  * Copyright(c) 2006-2007, Ext JS, LLC.
42244  *
42245  * Originally Released Under LGPL - original licence link has changed is not relivant.
42246  *
42247  * Fork - LGPL
42248  * <script type="text/javascript">
42249  */
42250 /**
42251  * @class Roo.form.Checkbox
42252  * @extends Roo.form.Field
42253  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42254  * @constructor
42255  * Creates a new Checkbox
42256  * @param {Object} config Configuration options
42257  */
42258 Roo.form.Checkbox = function(config){
42259     Roo.form.Checkbox.superclass.constructor.call(this, config);
42260     this.addEvents({
42261         /**
42262          * @event check
42263          * Fires when the checkbox is checked or unchecked.
42264              * @param {Roo.form.Checkbox} this This checkbox
42265              * @param {Boolean} checked The new checked value
42266              */
42267         check : true
42268     });
42269 };
42270
42271 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42272     /**
42273      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42274      */
42275     focusClass : undefined,
42276     /**
42277      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42278      */
42279     fieldClass: "x-form-field",
42280     /**
42281      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42282      */
42283     checked: false,
42284     /**
42285      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42286      * {tag: "input", type: "checkbox", autocomplete: "off"})
42287      */
42288     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42289     /**
42290      * @cfg {String} boxLabel The text that appears beside the checkbox
42291      */
42292     boxLabel : "",
42293     /**
42294      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42295      */  
42296     inputValue : '1',
42297     /**
42298      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42299      */
42300      valueOff: '0', // value when not checked..
42301
42302     actionMode : 'viewEl', 
42303     //
42304     // private
42305     itemCls : 'x-menu-check-item x-form-item',
42306     groupClass : 'x-menu-group-item',
42307     inputType : 'hidden',
42308     
42309     
42310     inSetChecked: false, // check that we are not calling self...
42311     
42312     inputElement: false, // real input element?
42313     basedOn: false, // ????
42314     
42315     isFormField: true, // not sure where this is needed!!!!
42316
42317     onResize : function(){
42318         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42319         if(!this.boxLabel){
42320             this.el.alignTo(this.wrap, 'c-c');
42321         }
42322     },
42323
42324     initEvents : function(){
42325         Roo.form.Checkbox.superclass.initEvents.call(this);
42326         this.el.on("click", this.onClick,  this);
42327         this.el.on("change", this.onClick,  this);
42328     },
42329
42330
42331     getResizeEl : function(){
42332         return this.wrap;
42333     },
42334
42335     getPositionEl : function(){
42336         return this.wrap;
42337     },
42338
42339     // private
42340     onRender : function(ct, position){
42341         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42342         /*
42343         if(this.inputValue !== undefined){
42344             this.el.dom.value = this.inputValue;
42345         }
42346         */
42347         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42348         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42349         var viewEl = this.wrap.createChild({ 
42350             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42351         this.viewEl = viewEl;   
42352         this.wrap.on('click', this.onClick,  this); 
42353         
42354         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42355         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42356         
42357         
42358         
42359         if(this.boxLabel){
42360             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42361         //    viewEl.on('click', this.onClick,  this); 
42362         }
42363         //if(this.checked){
42364             this.setChecked(this.checked);
42365         //}else{
42366             //this.checked = this.el.dom;
42367         //}
42368
42369     },
42370
42371     // private
42372     initValue : Roo.emptyFn,
42373
42374     /**
42375      * Returns the checked state of the checkbox.
42376      * @return {Boolean} True if checked, else false
42377      */
42378     getValue : function(){
42379         if(this.el){
42380             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42381         }
42382         return this.valueOff;
42383         
42384     },
42385
42386         // private
42387     onClick : function(){ 
42388         if (this.disabled) {
42389             return;
42390         }
42391         this.setChecked(!this.checked);
42392
42393         //if(this.el.dom.checked != this.checked){
42394         //    this.setValue(this.el.dom.checked);
42395        // }
42396     },
42397
42398     /**
42399      * Sets the checked state of the checkbox.
42400      * On is always based on a string comparison between inputValue and the param.
42401      * @param {Boolean/String} value - the value to set 
42402      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42403      */
42404     setValue : function(v,suppressEvent){
42405         
42406         
42407         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42408         //if(this.el && this.el.dom){
42409         //    this.el.dom.checked = this.checked;
42410         //    this.el.dom.defaultChecked = this.checked;
42411         //}
42412         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42413         //this.fireEvent("check", this, this.checked);
42414     },
42415     // private..
42416     setChecked : function(state,suppressEvent)
42417     {
42418         if (this.inSetChecked) {
42419             this.checked = state;
42420             return;
42421         }
42422         
42423     
42424         if(this.wrap){
42425             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42426         }
42427         this.checked = state;
42428         if(suppressEvent !== true){
42429             this.fireEvent('check', this, state);
42430         }
42431         this.inSetChecked = true;
42432         this.el.dom.value = state ? this.inputValue : this.valueOff;
42433         this.inSetChecked = false;
42434         
42435     },
42436     // handle setting of hidden value by some other method!!?!?
42437     setFromHidden: function()
42438     {
42439         if(!this.el){
42440             return;
42441         }
42442         //console.log("SET FROM HIDDEN");
42443         //alert('setFrom hidden');
42444         this.setValue(this.el.dom.value);
42445     },
42446     
42447     onDestroy : function()
42448     {
42449         if(this.viewEl){
42450             Roo.get(this.viewEl).remove();
42451         }
42452          
42453         Roo.form.Checkbox.superclass.onDestroy.call(this);
42454     },
42455     
42456     setBoxLabel : function(str)
42457     {
42458         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42459     }
42460
42461 });/*
42462  * Based on:
42463  * Ext JS Library 1.1.1
42464  * Copyright(c) 2006-2007, Ext JS, LLC.
42465  *
42466  * Originally Released Under LGPL - original licence link has changed is not relivant.
42467  *
42468  * Fork - LGPL
42469  * <script type="text/javascript">
42470  */
42471  
42472 /**
42473  * @class Roo.form.Radio
42474  * @extends Roo.form.Checkbox
42475  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42476  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42477  * @constructor
42478  * Creates a new Radio
42479  * @param {Object} config Configuration options
42480  */
42481 Roo.form.Radio = function(){
42482     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42483 };
42484 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42485     inputType: 'radio',
42486
42487     /**
42488      * If this radio is part of a group, it will return the selected value
42489      * @return {String}
42490      */
42491     getGroupValue : function(){
42492         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42493     },
42494     
42495     
42496     onRender : function(ct, position){
42497         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42498         
42499         if(this.inputValue !== undefined){
42500             this.el.dom.value = this.inputValue;
42501         }
42502          
42503         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42504         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42505         //var viewEl = this.wrap.createChild({ 
42506         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42507         //this.viewEl = viewEl;   
42508         //this.wrap.on('click', this.onClick,  this); 
42509         
42510         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42511         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42512         
42513         
42514         
42515         if(this.boxLabel){
42516             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42517         //    viewEl.on('click', this.onClick,  this); 
42518         }
42519          if(this.checked){
42520             this.el.dom.checked =   'checked' ;
42521         }
42522          
42523     } 
42524     
42525     
42526 });//<script type="text/javascript">
42527
42528 /*
42529  * Based  Ext JS Library 1.1.1
42530  * Copyright(c) 2006-2007, Ext JS, LLC.
42531  * LGPL
42532  *
42533  */
42534  
42535 /**
42536  * @class Roo.HtmlEditorCore
42537  * @extends Roo.Component
42538  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42539  *
42540  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42541  */
42542
42543 Roo.HtmlEditorCore = function(config){
42544     
42545     
42546     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42547     
42548     
42549     this.addEvents({
42550         /**
42551          * @event initialize
42552          * Fires when the editor is fully initialized (including the iframe)
42553          * @param {Roo.HtmlEditorCore} this
42554          */
42555         initialize: true,
42556         /**
42557          * @event activate
42558          * Fires when the editor is first receives the focus. Any insertion must wait
42559          * until after this event.
42560          * @param {Roo.HtmlEditorCore} this
42561          */
42562         activate: true,
42563          /**
42564          * @event beforesync
42565          * Fires before the textarea is updated with content from the editor iframe. Return false
42566          * to cancel the sync.
42567          * @param {Roo.HtmlEditorCore} this
42568          * @param {String} html
42569          */
42570         beforesync: true,
42571          /**
42572          * @event beforepush
42573          * Fires before the iframe editor is updated with content from the textarea. Return false
42574          * to cancel the push.
42575          * @param {Roo.HtmlEditorCore} this
42576          * @param {String} html
42577          */
42578         beforepush: true,
42579          /**
42580          * @event sync
42581          * Fires when the textarea is updated with content from the editor iframe.
42582          * @param {Roo.HtmlEditorCore} this
42583          * @param {String} html
42584          */
42585         sync: true,
42586          /**
42587          * @event push
42588          * Fires when the iframe editor is updated with content from the textarea.
42589          * @param {Roo.HtmlEditorCore} this
42590          * @param {String} html
42591          */
42592         push: true,
42593         
42594         /**
42595          * @event editorevent
42596          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42597          * @param {Roo.HtmlEditorCore} this
42598          */
42599         editorevent: true
42600         
42601     });
42602     
42603     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42604     
42605     // defaults : white / black...
42606     this.applyBlacklists();
42607     
42608     
42609     
42610 };
42611
42612
42613 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42614
42615
42616      /**
42617      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42618      */
42619     
42620     owner : false,
42621     
42622      /**
42623      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42624      *                        Roo.resizable.
42625      */
42626     resizable : false,
42627      /**
42628      * @cfg {Number} height (in pixels)
42629      */   
42630     height: 300,
42631    /**
42632      * @cfg {Number} width (in pixels)
42633      */   
42634     width: 500,
42635     
42636     /**
42637      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42638      * 
42639      */
42640     stylesheets: false,
42641     
42642     // id of frame..
42643     frameId: false,
42644     
42645     // private properties
42646     validationEvent : false,
42647     deferHeight: true,
42648     initialized : false,
42649     activated : false,
42650     sourceEditMode : false,
42651     onFocus : Roo.emptyFn,
42652     iframePad:3,
42653     hideMode:'offsets',
42654     
42655     clearUp: true,
42656     
42657     // blacklist + whitelisted elements..
42658     black: false,
42659     white: false,
42660      
42661     
42662
42663     /**
42664      * Protected method that will not generally be called directly. It
42665      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42666      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42667      */
42668     getDocMarkup : function(){
42669         // body styles..
42670         var st = '';
42671         
42672         // inherit styels from page...?? 
42673         if (this.stylesheets === false) {
42674             
42675             Roo.get(document.head).select('style').each(function(node) {
42676                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42677             });
42678             
42679             Roo.get(document.head).select('link').each(function(node) { 
42680                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42681             });
42682             
42683         } else if (!this.stylesheets.length) {
42684                 // simple..
42685                 st = '<style type="text/css">' +
42686                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42687                    '</style>';
42688         } else { 
42689             
42690         }
42691         
42692         st +=  '<style type="text/css">' +
42693             'IMG { cursor: pointer } ' +
42694         '</style>';
42695
42696         
42697         return '<html><head>' + st  +
42698             //<style type="text/css">' +
42699             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42700             //'</style>' +
42701             ' </head><body class="roo-htmleditor-body"></body></html>';
42702     },
42703
42704     // private
42705     onRender : function(ct, position)
42706     {
42707         var _t = this;
42708         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42709         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42710         
42711         
42712         this.el.dom.style.border = '0 none';
42713         this.el.dom.setAttribute('tabIndex', -1);
42714         this.el.addClass('x-hidden hide');
42715         
42716         
42717         
42718         if(Roo.isIE){ // fix IE 1px bogus margin
42719             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42720         }
42721        
42722         
42723         this.frameId = Roo.id();
42724         
42725          
42726         
42727         var iframe = this.owner.wrap.createChild({
42728             tag: 'iframe',
42729             cls: 'form-control', // bootstrap..
42730             id: this.frameId,
42731             name: this.frameId,
42732             frameBorder : 'no',
42733             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42734         }, this.el
42735         );
42736         
42737         
42738         this.iframe = iframe.dom;
42739
42740          this.assignDocWin();
42741         
42742         this.doc.designMode = 'on';
42743        
42744         this.doc.open();
42745         this.doc.write(this.getDocMarkup());
42746         this.doc.close();
42747
42748         
42749         var task = { // must defer to wait for browser to be ready
42750             run : function(){
42751                 //console.log("run task?" + this.doc.readyState);
42752                 this.assignDocWin();
42753                 if(this.doc.body || this.doc.readyState == 'complete'){
42754                     try {
42755                         this.doc.designMode="on";
42756                     } catch (e) {
42757                         return;
42758                     }
42759                     Roo.TaskMgr.stop(task);
42760                     this.initEditor.defer(10, this);
42761                 }
42762             },
42763             interval : 10,
42764             duration: 10000,
42765             scope: this
42766         };
42767         Roo.TaskMgr.start(task);
42768
42769     },
42770
42771     // private
42772     onResize : function(w, h)
42773     {
42774          Roo.log('resize: ' +w + ',' + h );
42775         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42776         if(!this.iframe){
42777             return;
42778         }
42779         if(typeof w == 'number'){
42780             
42781             this.iframe.style.width = w + 'px';
42782         }
42783         if(typeof h == 'number'){
42784             
42785             this.iframe.style.height = h + 'px';
42786             if(this.doc){
42787                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42788             }
42789         }
42790         
42791     },
42792
42793     /**
42794      * Toggles the editor between standard and source edit mode.
42795      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42796      */
42797     toggleSourceEdit : function(sourceEditMode){
42798         
42799         this.sourceEditMode = sourceEditMode === true;
42800         
42801         if(this.sourceEditMode){
42802  
42803             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42804             
42805         }else{
42806             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42807             //this.iframe.className = '';
42808             this.deferFocus();
42809         }
42810         //this.setSize(this.owner.wrap.getSize());
42811         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42812     },
42813
42814     
42815   
42816
42817     /**
42818      * Protected method that will not generally be called directly. If you need/want
42819      * custom HTML cleanup, this is the method you should override.
42820      * @param {String} html The HTML to be cleaned
42821      * return {String} The cleaned HTML
42822      */
42823     cleanHtml : function(html){
42824         html = String(html);
42825         if(html.length > 5){
42826             if(Roo.isSafari){ // strip safari nonsense
42827                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42828             }
42829         }
42830         if(html == '&nbsp;'){
42831             html = '';
42832         }
42833         return html;
42834     },
42835
42836     /**
42837      * HTML Editor -> Textarea
42838      * Protected method that will not generally be called directly. Syncs the contents
42839      * of the editor iframe with the textarea.
42840      */
42841     syncValue : function(){
42842         if(this.initialized){
42843             var bd = (this.doc.body || this.doc.documentElement);
42844             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42845             var html = bd.innerHTML;
42846             if(Roo.isSafari){
42847                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42848                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42849                 if(m && m[1]){
42850                     html = '<div style="'+m[0]+'">' + html + '</div>';
42851                 }
42852             }
42853             html = this.cleanHtml(html);
42854             // fix up the special chars.. normaly like back quotes in word...
42855             // however we do not want to do this with chinese..
42856             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42857                 var cc = b.charCodeAt();
42858                 if (
42859                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42860                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42861                     (cc >= 0xf900 && cc < 0xfb00 )
42862                 ) {
42863                         return b;
42864                 }
42865                 return "&#"+cc+";" 
42866             });
42867             if(this.owner.fireEvent('beforesync', this, html) !== false){
42868                 this.el.dom.value = html;
42869                 this.owner.fireEvent('sync', this, html);
42870             }
42871         }
42872     },
42873
42874     /**
42875      * Protected method that will not generally be called directly. Pushes the value of the textarea
42876      * into the iframe editor.
42877      */
42878     pushValue : function(){
42879         if(this.initialized){
42880             var v = this.el.dom.value.trim();
42881             
42882 //            if(v.length < 1){
42883 //                v = '&#160;';
42884 //            }
42885             
42886             if(this.owner.fireEvent('beforepush', this, v) !== false){
42887                 var d = (this.doc.body || this.doc.documentElement);
42888                 d.innerHTML = v;
42889                 this.cleanUpPaste();
42890                 this.el.dom.value = d.innerHTML;
42891                 this.owner.fireEvent('push', this, v);
42892             }
42893         }
42894     },
42895
42896     // private
42897     deferFocus : function(){
42898         this.focus.defer(10, this);
42899     },
42900
42901     // doc'ed in Field
42902     focus : function(){
42903         if(this.win && !this.sourceEditMode){
42904             this.win.focus();
42905         }else{
42906             this.el.focus();
42907         }
42908     },
42909     
42910     assignDocWin: function()
42911     {
42912         var iframe = this.iframe;
42913         
42914          if(Roo.isIE){
42915             this.doc = iframe.contentWindow.document;
42916             this.win = iframe.contentWindow;
42917         } else {
42918 //            if (!Roo.get(this.frameId)) {
42919 //                return;
42920 //            }
42921 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
42922 //            this.win = Roo.get(this.frameId).dom.contentWindow;
42923             
42924             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
42925                 return;
42926             }
42927             
42928             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
42929             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
42930         }
42931     },
42932     
42933     // private
42934     initEditor : function(){
42935         //console.log("INIT EDITOR");
42936         this.assignDocWin();
42937         
42938         
42939         
42940         this.doc.designMode="on";
42941         this.doc.open();
42942         this.doc.write(this.getDocMarkup());
42943         this.doc.close();
42944         
42945         var dbody = (this.doc.body || this.doc.documentElement);
42946         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
42947         // this copies styles from the containing element into thsi one..
42948         // not sure why we need all of this..
42949         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
42950         
42951         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
42952         //ss['background-attachment'] = 'fixed'; // w3c
42953         dbody.bgProperties = 'fixed'; // ie
42954         //Roo.DomHelper.applyStyles(dbody, ss);
42955         Roo.EventManager.on(this.doc, {
42956             //'mousedown': this.onEditorEvent,
42957             'mouseup': this.onEditorEvent,
42958             'dblclick': this.onEditorEvent,
42959             'click': this.onEditorEvent,
42960             'keyup': this.onEditorEvent,
42961             buffer:100,
42962             scope: this
42963         });
42964         if(Roo.isGecko){
42965             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
42966         }
42967         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
42968             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
42969         }
42970         this.initialized = true;
42971
42972         this.owner.fireEvent('initialize', this);
42973         this.pushValue();
42974     },
42975
42976     // private
42977     onDestroy : function(){
42978         
42979         
42980         
42981         if(this.rendered){
42982             
42983             //for (var i =0; i < this.toolbars.length;i++) {
42984             //    // fixme - ask toolbars for heights?
42985             //    this.toolbars[i].onDestroy();
42986            // }
42987             
42988             //this.wrap.dom.innerHTML = '';
42989             //this.wrap.remove();
42990         }
42991     },
42992
42993     // private
42994     onFirstFocus : function(){
42995         
42996         this.assignDocWin();
42997         
42998         
42999         this.activated = true;
43000          
43001     
43002         if(Roo.isGecko){ // prevent silly gecko errors
43003             this.win.focus();
43004             var s = this.win.getSelection();
43005             if(!s.focusNode || s.focusNode.nodeType != 3){
43006                 var r = s.getRangeAt(0);
43007                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43008                 r.collapse(true);
43009                 this.deferFocus();
43010             }
43011             try{
43012                 this.execCmd('useCSS', true);
43013                 this.execCmd('styleWithCSS', false);
43014             }catch(e){}
43015         }
43016         this.owner.fireEvent('activate', this);
43017     },
43018
43019     // private
43020     adjustFont: function(btn){
43021         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43022         //if(Roo.isSafari){ // safari
43023         //    adjust *= 2;
43024        // }
43025         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43026         if(Roo.isSafari){ // safari
43027             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43028             v =  (v < 10) ? 10 : v;
43029             v =  (v > 48) ? 48 : v;
43030             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43031             
43032         }
43033         
43034         
43035         v = Math.max(1, v+adjust);
43036         
43037         this.execCmd('FontSize', v  );
43038     },
43039
43040     onEditorEvent : function(e)
43041     {
43042         this.owner.fireEvent('editorevent', this, e);
43043       //  this.updateToolbar();
43044         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43045     },
43046
43047     insertTag : function(tg)
43048     {
43049         // could be a bit smarter... -> wrap the current selected tRoo..
43050         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43051             
43052             range = this.createRange(this.getSelection());
43053             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43054             wrappingNode.appendChild(range.extractContents());
43055             range.insertNode(wrappingNode);
43056
43057             return;
43058             
43059             
43060             
43061         }
43062         this.execCmd("formatblock",   tg);
43063         
43064     },
43065     
43066     insertText : function(txt)
43067     {
43068         
43069         
43070         var range = this.createRange();
43071         range.deleteContents();
43072                //alert(Sender.getAttribute('label'));
43073                
43074         range.insertNode(this.doc.createTextNode(txt));
43075     } ,
43076     
43077      
43078
43079     /**
43080      * Executes a Midas editor command on the editor document and performs necessary focus and
43081      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43082      * @param {String} cmd The Midas command
43083      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43084      */
43085     relayCmd : function(cmd, value){
43086         this.win.focus();
43087         this.execCmd(cmd, value);
43088         this.owner.fireEvent('editorevent', this);
43089         //this.updateToolbar();
43090         this.owner.deferFocus();
43091     },
43092
43093     /**
43094      * Executes a Midas editor command directly on the editor document.
43095      * For visual commands, you should use {@link #relayCmd} instead.
43096      * <b>This should only be called after the editor is initialized.</b>
43097      * @param {String} cmd The Midas command
43098      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43099      */
43100     execCmd : function(cmd, value){
43101         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43102         this.syncValue();
43103     },
43104  
43105  
43106    
43107     /**
43108      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43109      * to insert tRoo.
43110      * @param {String} text | dom node.. 
43111      */
43112     insertAtCursor : function(text)
43113     {
43114         
43115         
43116         
43117         if(!this.activated){
43118             return;
43119         }
43120         /*
43121         if(Roo.isIE){
43122             this.win.focus();
43123             var r = this.doc.selection.createRange();
43124             if(r){
43125                 r.collapse(true);
43126                 r.pasteHTML(text);
43127                 this.syncValue();
43128                 this.deferFocus();
43129             
43130             }
43131             return;
43132         }
43133         */
43134         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43135             this.win.focus();
43136             
43137             
43138             // from jquery ui (MIT licenced)
43139             var range, node;
43140             var win = this.win;
43141             
43142             if (win.getSelection && win.getSelection().getRangeAt) {
43143                 range = win.getSelection().getRangeAt(0);
43144                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43145                 range.insertNode(node);
43146             } else if (win.document.selection && win.document.selection.createRange) {
43147                 // no firefox support
43148                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43149                 win.document.selection.createRange().pasteHTML(txt);
43150             } else {
43151                 // no firefox support
43152                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43153                 this.execCmd('InsertHTML', txt);
43154             } 
43155             
43156             this.syncValue();
43157             
43158             this.deferFocus();
43159         }
43160     },
43161  // private
43162     mozKeyPress : function(e){
43163         if(e.ctrlKey){
43164             var c = e.getCharCode(), cmd;
43165           
43166             if(c > 0){
43167                 c = String.fromCharCode(c).toLowerCase();
43168                 switch(c){
43169                     case 'b':
43170                         cmd = 'bold';
43171                         break;
43172                     case 'i':
43173                         cmd = 'italic';
43174                         break;
43175                     
43176                     case 'u':
43177                         cmd = 'underline';
43178                         break;
43179                     
43180                     case 'v':
43181                         this.cleanUpPaste.defer(100, this);
43182                         return;
43183                         
43184                 }
43185                 if(cmd){
43186                     this.win.focus();
43187                     this.execCmd(cmd);
43188                     this.deferFocus();
43189                     e.preventDefault();
43190                 }
43191                 
43192             }
43193         }
43194     },
43195
43196     // private
43197     fixKeys : function(){ // load time branching for fastest keydown performance
43198         if(Roo.isIE){
43199             return function(e){
43200                 var k = e.getKey(), r;
43201                 if(k == e.TAB){
43202                     e.stopEvent();
43203                     r = this.doc.selection.createRange();
43204                     if(r){
43205                         r.collapse(true);
43206                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43207                         this.deferFocus();
43208                     }
43209                     return;
43210                 }
43211                 
43212                 if(k == e.ENTER){
43213                     r = this.doc.selection.createRange();
43214                     if(r){
43215                         var target = r.parentElement();
43216                         if(!target || target.tagName.toLowerCase() != 'li'){
43217                             e.stopEvent();
43218                             r.pasteHTML('<br />');
43219                             r.collapse(false);
43220                             r.select();
43221                         }
43222                     }
43223                 }
43224                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43225                     this.cleanUpPaste.defer(100, this);
43226                     return;
43227                 }
43228                 
43229                 
43230             };
43231         }else if(Roo.isOpera){
43232             return function(e){
43233                 var k = e.getKey();
43234                 if(k == e.TAB){
43235                     e.stopEvent();
43236                     this.win.focus();
43237                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43238                     this.deferFocus();
43239                 }
43240                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43241                     this.cleanUpPaste.defer(100, this);
43242                     return;
43243                 }
43244                 
43245             };
43246         }else if(Roo.isSafari){
43247             return function(e){
43248                 var k = e.getKey();
43249                 
43250                 if(k == e.TAB){
43251                     e.stopEvent();
43252                     this.execCmd('InsertText','\t');
43253                     this.deferFocus();
43254                     return;
43255                 }
43256                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43257                     this.cleanUpPaste.defer(100, this);
43258                     return;
43259                 }
43260                 
43261              };
43262         }
43263     }(),
43264     
43265     getAllAncestors: function()
43266     {
43267         var p = this.getSelectedNode();
43268         var a = [];
43269         if (!p) {
43270             a.push(p); // push blank onto stack..
43271             p = this.getParentElement();
43272         }
43273         
43274         
43275         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43276             a.push(p);
43277             p = p.parentNode;
43278         }
43279         a.push(this.doc.body);
43280         return a;
43281     },
43282     lastSel : false,
43283     lastSelNode : false,
43284     
43285     
43286     getSelection : function() 
43287     {
43288         this.assignDocWin();
43289         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43290     },
43291     
43292     getSelectedNode: function() 
43293     {
43294         // this may only work on Gecko!!!
43295         
43296         // should we cache this!!!!
43297         
43298         
43299         
43300          
43301         var range = this.createRange(this.getSelection()).cloneRange();
43302         
43303         if (Roo.isIE) {
43304             var parent = range.parentElement();
43305             while (true) {
43306                 var testRange = range.duplicate();
43307                 testRange.moveToElementText(parent);
43308                 if (testRange.inRange(range)) {
43309                     break;
43310                 }
43311                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43312                     break;
43313                 }
43314                 parent = parent.parentElement;
43315             }
43316             return parent;
43317         }
43318         
43319         // is ancestor a text element.
43320         var ac =  range.commonAncestorContainer;
43321         if (ac.nodeType == 3) {
43322             ac = ac.parentNode;
43323         }
43324         
43325         var ar = ac.childNodes;
43326          
43327         var nodes = [];
43328         var other_nodes = [];
43329         var has_other_nodes = false;
43330         for (var i=0;i<ar.length;i++) {
43331             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43332                 continue;
43333             }
43334             // fullly contained node.
43335             
43336             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43337                 nodes.push(ar[i]);
43338                 continue;
43339             }
43340             
43341             // probably selected..
43342             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43343                 other_nodes.push(ar[i]);
43344                 continue;
43345             }
43346             // outer..
43347             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43348                 continue;
43349             }
43350             
43351             
43352             has_other_nodes = true;
43353         }
43354         if (!nodes.length && other_nodes.length) {
43355             nodes= other_nodes;
43356         }
43357         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43358             return false;
43359         }
43360         
43361         return nodes[0];
43362     },
43363     createRange: function(sel)
43364     {
43365         // this has strange effects when using with 
43366         // top toolbar - not sure if it's a great idea.
43367         //this.editor.contentWindow.focus();
43368         if (typeof sel != "undefined") {
43369             try {
43370                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43371             } catch(e) {
43372                 return this.doc.createRange();
43373             }
43374         } else {
43375             return this.doc.createRange();
43376         }
43377     },
43378     getParentElement: function()
43379     {
43380         
43381         this.assignDocWin();
43382         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43383         
43384         var range = this.createRange(sel);
43385          
43386         try {
43387             var p = range.commonAncestorContainer;
43388             while (p.nodeType == 3) { // text node
43389                 p = p.parentNode;
43390             }
43391             return p;
43392         } catch (e) {
43393             return null;
43394         }
43395     
43396     },
43397     /***
43398      *
43399      * Range intersection.. the hard stuff...
43400      *  '-1' = before
43401      *  '0' = hits..
43402      *  '1' = after.
43403      *         [ -- selected range --- ]
43404      *   [fail]                        [fail]
43405      *
43406      *    basically..
43407      *      if end is before start or  hits it. fail.
43408      *      if start is after end or hits it fail.
43409      *
43410      *   if either hits (but other is outside. - then it's not 
43411      *   
43412      *    
43413      **/
43414     
43415     
43416     // @see http://www.thismuchiknow.co.uk/?p=64.
43417     rangeIntersectsNode : function(range, node)
43418     {
43419         var nodeRange = node.ownerDocument.createRange();
43420         try {
43421             nodeRange.selectNode(node);
43422         } catch (e) {
43423             nodeRange.selectNodeContents(node);
43424         }
43425     
43426         var rangeStartRange = range.cloneRange();
43427         rangeStartRange.collapse(true);
43428     
43429         var rangeEndRange = range.cloneRange();
43430         rangeEndRange.collapse(false);
43431     
43432         var nodeStartRange = nodeRange.cloneRange();
43433         nodeStartRange.collapse(true);
43434     
43435         var nodeEndRange = nodeRange.cloneRange();
43436         nodeEndRange.collapse(false);
43437     
43438         return rangeStartRange.compareBoundaryPoints(
43439                  Range.START_TO_START, nodeEndRange) == -1 &&
43440                rangeEndRange.compareBoundaryPoints(
43441                  Range.START_TO_START, nodeStartRange) == 1;
43442         
43443          
43444     },
43445     rangeCompareNode : function(range, node)
43446     {
43447         var nodeRange = node.ownerDocument.createRange();
43448         try {
43449             nodeRange.selectNode(node);
43450         } catch (e) {
43451             nodeRange.selectNodeContents(node);
43452         }
43453         
43454         
43455         range.collapse(true);
43456     
43457         nodeRange.collapse(true);
43458      
43459         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43460         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43461          
43462         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43463         
43464         var nodeIsBefore   =  ss == 1;
43465         var nodeIsAfter    = ee == -1;
43466         
43467         if (nodeIsBefore && nodeIsAfter) {
43468             return 0; // outer
43469         }
43470         if (!nodeIsBefore && nodeIsAfter) {
43471             return 1; //right trailed.
43472         }
43473         
43474         if (nodeIsBefore && !nodeIsAfter) {
43475             return 2;  // left trailed.
43476         }
43477         // fully contined.
43478         return 3;
43479     },
43480
43481     // private? - in a new class?
43482     cleanUpPaste :  function()
43483     {
43484         // cleans up the whole document..
43485         Roo.log('cleanuppaste');
43486         
43487         this.cleanUpChildren(this.doc.body);
43488         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43489         if (clean != this.doc.body.innerHTML) {
43490             this.doc.body.innerHTML = clean;
43491         }
43492         
43493     },
43494     
43495     cleanWordChars : function(input) {// change the chars to hex code
43496         var he = Roo.HtmlEditorCore;
43497         
43498         var output = input;
43499         Roo.each(he.swapCodes, function(sw) { 
43500             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43501             
43502             output = output.replace(swapper, sw[1]);
43503         });
43504         
43505         return output;
43506     },
43507     
43508     
43509     cleanUpChildren : function (n)
43510     {
43511         if (!n.childNodes.length) {
43512             return;
43513         }
43514         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43515            this.cleanUpChild(n.childNodes[i]);
43516         }
43517     },
43518     
43519     
43520         
43521     
43522     cleanUpChild : function (node)
43523     {
43524         var ed = this;
43525         //console.log(node);
43526         if (node.nodeName == "#text") {
43527             // clean up silly Windows -- stuff?
43528             return; 
43529         }
43530         if (node.nodeName == "#comment") {
43531             node.parentNode.removeChild(node);
43532             // clean up silly Windows -- stuff?
43533             return; 
43534         }
43535         var lcname = node.tagName.toLowerCase();
43536         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43537         // whitelist of tags..
43538         
43539         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43540             // remove node.
43541             node.parentNode.removeChild(node);
43542             return;
43543             
43544         }
43545         
43546         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43547         
43548         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43549         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43550         
43551         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43552         //    remove_keep_children = true;
43553         //}
43554         
43555         if (remove_keep_children) {
43556             this.cleanUpChildren(node);
43557             // inserts everything just before this node...
43558             while (node.childNodes.length) {
43559                 var cn = node.childNodes[0];
43560                 node.removeChild(cn);
43561                 node.parentNode.insertBefore(cn, node);
43562             }
43563             node.parentNode.removeChild(node);
43564             return;
43565         }
43566         
43567         if (!node.attributes || !node.attributes.length) {
43568             this.cleanUpChildren(node);
43569             return;
43570         }
43571         
43572         function cleanAttr(n,v)
43573         {
43574             
43575             if (v.match(/^\./) || v.match(/^\//)) {
43576                 return;
43577             }
43578             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43579                 return;
43580             }
43581             if (v.match(/^#/)) {
43582                 return;
43583             }
43584 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43585             node.removeAttribute(n);
43586             
43587         }
43588         
43589         var cwhite = this.cwhite;
43590         var cblack = this.cblack;
43591             
43592         function cleanStyle(n,v)
43593         {
43594             if (v.match(/expression/)) { //XSS?? should we even bother..
43595                 node.removeAttribute(n);
43596                 return;
43597             }
43598             
43599             var parts = v.split(/;/);
43600             var clean = [];
43601             
43602             Roo.each(parts, function(p) {
43603                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43604                 if (!p.length) {
43605                     return true;
43606                 }
43607                 var l = p.split(':').shift().replace(/\s+/g,'');
43608                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43609                 
43610                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43611 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43612                     //node.removeAttribute(n);
43613                     return true;
43614                 }
43615                 //Roo.log()
43616                 // only allow 'c whitelisted system attributes'
43617                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43618 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43619                     //node.removeAttribute(n);
43620                     return true;
43621                 }
43622                 
43623                 
43624                  
43625                 
43626                 clean.push(p);
43627                 return true;
43628             });
43629             if (clean.length) { 
43630                 node.setAttribute(n, clean.join(';'));
43631             } else {
43632                 node.removeAttribute(n);
43633             }
43634             
43635         }
43636         
43637         
43638         for (var i = node.attributes.length-1; i > -1 ; i--) {
43639             var a = node.attributes[i];
43640             //console.log(a);
43641             
43642             if (a.name.toLowerCase().substr(0,2)=='on')  {
43643                 node.removeAttribute(a.name);
43644                 continue;
43645             }
43646             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43647                 node.removeAttribute(a.name);
43648                 continue;
43649             }
43650             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43651                 cleanAttr(a.name,a.value); // fixme..
43652                 continue;
43653             }
43654             if (a.name == 'style') {
43655                 cleanStyle(a.name,a.value);
43656                 continue;
43657             }
43658             /// clean up MS crap..
43659             // tecnically this should be a list of valid class'es..
43660             
43661             
43662             if (a.name == 'class') {
43663                 if (a.value.match(/^Mso/)) {
43664                     node.className = '';
43665                 }
43666                 
43667                 if (a.value.match(/body/)) {
43668                     node.className = '';
43669                 }
43670                 continue;
43671             }
43672             
43673             // style cleanup!?
43674             // class cleanup?
43675             
43676         }
43677         
43678         
43679         this.cleanUpChildren(node);
43680         
43681         
43682     },
43683     
43684     /**
43685      * Clean up MS wordisms...
43686      */
43687     cleanWord : function(node)
43688     {
43689         
43690         
43691         if (!node) {
43692             this.cleanWord(this.doc.body);
43693             return;
43694         }
43695         if (node.nodeName == "#text") {
43696             // clean up silly Windows -- stuff?
43697             return; 
43698         }
43699         if (node.nodeName == "#comment") {
43700             node.parentNode.removeChild(node);
43701             // clean up silly Windows -- stuff?
43702             return; 
43703         }
43704         
43705         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43706             node.parentNode.removeChild(node);
43707             return;
43708         }
43709         
43710         // remove - but keep children..
43711         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43712             while (node.childNodes.length) {
43713                 var cn = node.childNodes[0];
43714                 node.removeChild(cn);
43715                 node.parentNode.insertBefore(cn, node);
43716             }
43717             node.parentNode.removeChild(node);
43718             this.iterateChildren(node, this.cleanWord);
43719             return;
43720         }
43721         // clean styles
43722         if (node.className.length) {
43723             
43724             var cn = node.className.split(/\W+/);
43725             var cna = [];
43726             Roo.each(cn, function(cls) {
43727                 if (cls.match(/Mso[a-zA-Z]+/)) {
43728                     return;
43729                 }
43730                 cna.push(cls);
43731             });
43732             node.className = cna.length ? cna.join(' ') : '';
43733             if (!cna.length) {
43734                 node.removeAttribute("class");
43735             }
43736         }
43737         
43738         if (node.hasAttribute("lang")) {
43739             node.removeAttribute("lang");
43740         }
43741         
43742         if (node.hasAttribute("style")) {
43743             
43744             var styles = node.getAttribute("style").split(";");
43745             var nstyle = [];
43746             Roo.each(styles, function(s) {
43747                 if (!s.match(/:/)) {
43748                     return;
43749                 }
43750                 var kv = s.split(":");
43751                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43752                     return;
43753                 }
43754                 // what ever is left... we allow.
43755                 nstyle.push(s);
43756             });
43757             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43758             if (!nstyle.length) {
43759                 node.removeAttribute('style');
43760             }
43761         }
43762         this.iterateChildren(node, this.cleanWord);
43763         
43764         
43765         
43766     },
43767     /**
43768      * iterateChildren of a Node, calling fn each time, using this as the scole..
43769      * @param {DomNode} node node to iterate children of.
43770      * @param {Function} fn method of this class to call on each item.
43771      */
43772     iterateChildren : function(node, fn)
43773     {
43774         if (!node.childNodes.length) {
43775                 return;
43776         }
43777         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43778            fn.call(this, node.childNodes[i])
43779         }
43780     },
43781     
43782     
43783     /**
43784      * cleanTableWidths.
43785      *
43786      * Quite often pasting from word etc.. results in tables with column and widths.
43787      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43788      *
43789      */
43790     cleanTableWidths : function(node)
43791     {
43792          
43793          
43794         if (!node) {
43795             this.cleanTableWidths(this.doc.body);
43796             return;
43797         }
43798         
43799         // ignore list...
43800         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43801             return; 
43802         }
43803         Roo.log(node.tagName);
43804         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43805             this.iterateChildren(node, this.cleanTableWidths);
43806             return;
43807         }
43808         if (node.hasAttribute('width')) {
43809             node.removeAttribute('width');
43810         }
43811         
43812          
43813         if (node.hasAttribute("style")) {
43814             // pretty basic...
43815             
43816             var styles = node.getAttribute("style").split(";");
43817             var nstyle = [];
43818             Roo.each(styles, function(s) {
43819                 if (!s.match(/:/)) {
43820                     return;
43821                 }
43822                 var kv = s.split(":");
43823                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43824                     return;
43825                 }
43826                 // what ever is left... we allow.
43827                 nstyle.push(s);
43828             });
43829             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43830             if (!nstyle.length) {
43831                 node.removeAttribute('style');
43832             }
43833         }
43834         
43835         this.iterateChildren(node, this.cleanTableWidths);
43836         
43837         
43838     },
43839     
43840     
43841     
43842     
43843     domToHTML : function(currentElement, depth, nopadtext) {
43844         
43845         depth = depth || 0;
43846         nopadtext = nopadtext || false;
43847     
43848         if (!currentElement) {
43849             return this.domToHTML(this.doc.body);
43850         }
43851         
43852         //Roo.log(currentElement);
43853         var j;
43854         var allText = false;
43855         var nodeName = currentElement.nodeName;
43856         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43857         
43858         if  (nodeName == '#text') {
43859             
43860             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43861         }
43862         
43863         
43864         var ret = '';
43865         if (nodeName != 'BODY') {
43866              
43867             var i = 0;
43868             // Prints the node tagName, such as <A>, <IMG>, etc
43869             if (tagName) {
43870                 var attr = [];
43871                 for(i = 0; i < currentElement.attributes.length;i++) {
43872                     // quoting?
43873                     var aname = currentElement.attributes.item(i).name;
43874                     if (!currentElement.attributes.item(i).value.length) {
43875                         continue;
43876                     }
43877                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
43878                 }
43879                 
43880                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
43881             } 
43882             else {
43883                 
43884                 // eack
43885             }
43886         } else {
43887             tagName = false;
43888         }
43889         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
43890             return ret;
43891         }
43892         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
43893             nopadtext = true;
43894         }
43895         
43896         
43897         // Traverse the tree
43898         i = 0;
43899         var currentElementChild = currentElement.childNodes.item(i);
43900         var allText = true;
43901         var innerHTML  = '';
43902         lastnode = '';
43903         while (currentElementChild) {
43904             // Formatting code (indent the tree so it looks nice on the screen)
43905             var nopad = nopadtext;
43906             if (lastnode == 'SPAN') {
43907                 nopad  = true;
43908             }
43909             // text
43910             if  (currentElementChild.nodeName == '#text') {
43911                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
43912                 toadd = nopadtext ? toadd : toadd.trim();
43913                 if (!nopad && toadd.length > 80) {
43914                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
43915                 }
43916                 innerHTML  += toadd;
43917                 
43918                 i++;
43919                 currentElementChild = currentElement.childNodes.item(i);
43920                 lastNode = '';
43921                 continue;
43922             }
43923             allText = false;
43924             
43925             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
43926                 
43927             // Recursively traverse the tree structure of the child node
43928             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
43929             lastnode = currentElementChild.nodeName;
43930             i++;
43931             currentElementChild=currentElement.childNodes.item(i);
43932         }
43933         
43934         ret += innerHTML;
43935         
43936         if (!allText) {
43937                 // The remaining code is mostly for formatting the tree
43938             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
43939         }
43940         
43941         
43942         if (tagName) {
43943             ret+= "</"+tagName+">";
43944         }
43945         return ret;
43946         
43947     },
43948         
43949     applyBlacklists : function()
43950     {
43951         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
43952         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
43953         
43954         this.white = [];
43955         this.black = [];
43956         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
43957             if (b.indexOf(tag) > -1) {
43958                 return;
43959             }
43960             this.white.push(tag);
43961             
43962         }, this);
43963         
43964         Roo.each(w, function(tag) {
43965             if (b.indexOf(tag) > -1) {
43966                 return;
43967             }
43968             if (this.white.indexOf(tag) > -1) {
43969                 return;
43970             }
43971             this.white.push(tag);
43972             
43973         }, this);
43974         
43975         
43976         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
43977             if (w.indexOf(tag) > -1) {
43978                 return;
43979             }
43980             this.black.push(tag);
43981             
43982         }, this);
43983         
43984         Roo.each(b, function(tag) {
43985             if (w.indexOf(tag) > -1) {
43986                 return;
43987             }
43988             if (this.black.indexOf(tag) > -1) {
43989                 return;
43990             }
43991             this.black.push(tag);
43992             
43993         }, this);
43994         
43995         
43996         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
43997         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
43998         
43999         this.cwhite = [];
44000         this.cblack = [];
44001         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44002             if (b.indexOf(tag) > -1) {
44003                 return;
44004             }
44005             this.cwhite.push(tag);
44006             
44007         }, this);
44008         
44009         Roo.each(w, function(tag) {
44010             if (b.indexOf(tag) > -1) {
44011                 return;
44012             }
44013             if (this.cwhite.indexOf(tag) > -1) {
44014                 return;
44015             }
44016             this.cwhite.push(tag);
44017             
44018         }, this);
44019         
44020         
44021         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44022             if (w.indexOf(tag) > -1) {
44023                 return;
44024             }
44025             this.cblack.push(tag);
44026             
44027         }, this);
44028         
44029         Roo.each(b, function(tag) {
44030             if (w.indexOf(tag) > -1) {
44031                 return;
44032             }
44033             if (this.cblack.indexOf(tag) > -1) {
44034                 return;
44035             }
44036             this.cblack.push(tag);
44037             
44038         }, this);
44039     },
44040     
44041     setStylesheets : function(stylesheets)
44042     {
44043         if(typeof(stylesheets) == 'string'){
44044             Roo.get(this.iframe.contentDocument.head).createChild({
44045                 tag : 'link',
44046                 rel : 'stylesheet',
44047                 type : 'text/css',
44048                 href : stylesheets
44049             });
44050             
44051             return;
44052         }
44053         var _this = this;
44054      
44055         Roo.each(stylesheets, function(s) {
44056             if(!s.length){
44057                 return;
44058             }
44059             
44060             Roo.get(_this.iframe.contentDocument.head).createChild({
44061                 tag : 'link',
44062                 rel : 'stylesheet',
44063                 type : 'text/css',
44064                 href : s
44065             });
44066         });
44067
44068         
44069     },
44070     
44071     removeStylesheets : function()
44072     {
44073         var _this = this;
44074         
44075         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44076             s.remove();
44077         });
44078     }
44079     
44080     // hide stuff that is not compatible
44081     /**
44082      * @event blur
44083      * @hide
44084      */
44085     /**
44086      * @event change
44087      * @hide
44088      */
44089     /**
44090      * @event focus
44091      * @hide
44092      */
44093     /**
44094      * @event specialkey
44095      * @hide
44096      */
44097     /**
44098      * @cfg {String} fieldClass @hide
44099      */
44100     /**
44101      * @cfg {String} focusClass @hide
44102      */
44103     /**
44104      * @cfg {String} autoCreate @hide
44105      */
44106     /**
44107      * @cfg {String} inputType @hide
44108      */
44109     /**
44110      * @cfg {String} invalidClass @hide
44111      */
44112     /**
44113      * @cfg {String} invalidText @hide
44114      */
44115     /**
44116      * @cfg {String} msgFx @hide
44117      */
44118     /**
44119      * @cfg {String} validateOnBlur @hide
44120      */
44121 });
44122
44123 Roo.HtmlEditorCore.white = [
44124         'area', 'br', 'img', 'input', 'hr', 'wbr',
44125         
44126        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44127        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44128        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44129        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44130        'table',   'ul',         'xmp', 
44131        
44132        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44133       'thead',   'tr', 
44134      
44135       'dir', 'menu', 'ol', 'ul', 'dl',
44136        
44137       'embed',  'object'
44138 ];
44139
44140
44141 Roo.HtmlEditorCore.black = [
44142     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44143         'applet', // 
44144         'base',   'basefont', 'bgsound', 'blink',  'body', 
44145         'frame',  'frameset', 'head',    'html',   'ilayer', 
44146         'iframe', 'layer',  'link',     'meta',    'object',   
44147         'script', 'style' ,'title',  'xml' // clean later..
44148 ];
44149 Roo.HtmlEditorCore.clean = [
44150     'script', 'style', 'title', 'xml'
44151 ];
44152 Roo.HtmlEditorCore.remove = [
44153     'font'
44154 ];
44155 // attributes..
44156
44157 Roo.HtmlEditorCore.ablack = [
44158     'on'
44159 ];
44160     
44161 Roo.HtmlEditorCore.aclean = [ 
44162     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44163 ];
44164
44165 // protocols..
44166 Roo.HtmlEditorCore.pwhite= [
44167         'http',  'https',  'mailto'
44168 ];
44169
44170 // white listed style attributes.
44171 Roo.HtmlEditorCore.cwhite= [
44172       //  'text-align', /// default is to allow most things..
44173       
44174          
44175 //        'font-size'//??
44176 ];
44177
44178 // black listed style attributes.
44179 Roo.HtmlEditorCore.cblack= [
44180       //  'font-size' -- this can be set by the project 
44181 ];
44182
44183
44184 Roo.HtmlEditorCore.swapCodes   =[ 
44185     [    8211, "--" ], 
44186     [    8212, "--" ], 
44187     [    8216,  "'" ],  
44188     [    8217, "'" ],  
44189     [    8220, '"' ],  
44190     [    8221, '"' ],  
44191     [    8226, "*" ],  
44192     [    8230, "..." ]
44193 ]; 
44194
44195     //<script type="text/javascript">
44196
44197 /*
44198  * Ext JS Library 1.1.1
44199  * Copyright(c) 2006-2007, Ext JS, LLC.
44200  * Licence LGPL
44201  * 
44202  */
44203  
44204  
44205 Roo.form.HtmlEditor = function(config){
44206     
44207     
44208     
44209     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44210     
44211     if (!this.toolbars) {
44212         this.toolbars = [];
44213     }
44214     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44215     
44216     
44217 };
44218
44219 /**
44220  * @class Roo.form.HtmlEditor
44221  * @extends Roo.form.Field
44222  * Provides a lightweight HTML Editor component.
44223  *
44224  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44225  * 
44226  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44227  * supported by this editor.</b><br/><br/>
44228  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44229  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44230  */
44231 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44232     /**
44233      * @cfg {Boolean} clearUp
44234      */
44235     clearUp : true,
44236       /**
44237      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44238      */
44239     toolbars : false,
44240    
44241      /**
44242      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44243      *                        Roo.resizable.
44244      */
44245     resizable : false,
44246      /**
44247      * @cfg {Number} height (in pixels)
44248      */   
44249     height: 300,
44250    /**
44251      * @cfg {Number} width (in pixels)
44252      */   
44253     width: 500,
44254     
44255     /**
44256      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44257      * 
44258      */
44259     stylesheets: false,
44260     
44261     
44262      /**
44263      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44264      * 
44265      */
44266     cblack: false,
44267     /**
44268      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44269      * 
44270      */
44271     cwhite: false,
44272     
44273      /**
44274      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44275      * 
44276      */
44277     black: false,
44278     /**
44279      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44280      * 
44281      */
44282     white: false,
44283     
44284     // id of frame..
44285     frameId: false,
44286     
44287     // private properties
44288     validationEvent : false,
44289     deferHeight: true,
44290     initialized : false,
44291     activated : false,
44292     
44293     onFocus : Roo.emptyFn,
44294     iframePad:3,
44295     hideMode:'offsets',
44296     
44297     actionMode : 'container', // defaults to hiding it...
44298     
44299     defaultAutoCreate : { // modified by initCompnoent..
44300         tag: "textarea",
44301         style:"width:500px;height:300px;",
44302         autocomplete: "new-password"
44303     },
44304
44305     // private
44306     initComponent : function(){
44307         this.addEvents({
44308             /**
44309              * @event initialize
44310              * Fires when the editor is fully initialized (including the iframe)
44311              * @param {HtmlEditor} this
44312              */
44313             initialize: true,
44314             /**
44315              * @event activate
44316              * Fires when the editor is first receives the focus. Any insertion must wait
44317              * until after this event.
44318              * @param {HtmlEditor} this
44319              */
44320             activate: true,
44321              /**
44322              * @event beforesync
44323              * Fires before the textarea is updated with content from the editor iframe. Return false
44324              * to cancel the sync.
44325              * @param {HtmlEditor} this
44326              * @param {String} html
44327              */
44328             beforesync: true,
44329              /**
44330              * @event beforepush
44331              * Fires before the iframe editor is updated with content from the textarea. Return false
44332              * to cancel the push.
44333              * @param {HtmlEditor} this
44334              * @param {String} html
44335              */
44336             beforepush: true,
44337              /**
44338              * @event sync
44339              * Fires when the textarea is updated with content from the editor iframe.
44340              * @param {HtmlEditor} this
44341              * @param {String} html
44342              */
44343             sync: true,
44344              /**
44345              * @event push
44346              * Fires when the iframe editor is updated with content from the textarea.
44347              * @param {HtmlEditor} this
44348              * @param {String} html
44349              */
44350             push: true,
44351              /**
44352              * @event editmodechange
44353              * Fires when the editor switches edit modes
44354              * @param {HtmlEditor} this
44355              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44356              */
44357             editmodechange: true,
44358             /**
44359              * @event editorevent
44360              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44361              * @param {HtmlEditor} this
44362              */
44363             editorevent: true,
44364             /**
44365              * @event firstfocus
44366              * Fires when on first focus - needed by toolbars..
44367              * @param {HtmlEditor} this
44368              */
44369             firstfocus: true,
44370             /**
44371              * @event autosave
44372              * Auto save the htmlEditor value as a file into Events
44373              * @param {HtmlEditor} this
44374              */
44375             autosave: true,
44376             /**
44377              * @event savedpreview
44378              * preview the saved version of htmlEditor
44379              * @param {HtmlEditor} this
44380              */
44381             savedpreview: true,
44382             
44383             /**
44384             * @event stylesheetsclick
44385             * Fires when press the Sytlesheets button
44386             * @param {Roo.HtmlEditorCore} this
44387             */
44388             stylesheetsclick: true
44389         });
44390         this.defaultAutoCreate =  {
44391             tag: "textarea",
44392             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44393             autocomplete: "new-password"
44394         };
44395     },
44396
44397     /**
44398      * Protected method that will not generally be called directly. It
44399      * is called when the editor creates its toolbar. Override this method if you need to
44400      * add custom toolbar buttons.
44401      * @param {HtmlEditor} editor
44402      */
44403     createToolbar : function(editor){
44404         Roo.log("create toolbars");
44405         if (!editor.toolbars || !editor.toolbars.length) {
44406             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44407         }
44408         
44409         for (var i =0 ; i < editor.toolbars.length;i++) {
44410             editor.toolbars[i] = Roo.factory(
44411                     typeof(editor.toolbars[i]) == 'string' ?
44412                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44413                 Roo.form.HtmlEditor);
44414             editor.toolbars[i].init(editor);
44415         }
44416          
44417         
44418     },
44419
44420      
44421     // private
44422     onRender : function(ct, position)
44423     {
44424         var _t = this;
44425         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44426         
44427         this.wrap = this.el.wrap({
44428             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44429         });
44430         
44431         this.editorcore.onRender(ct, position);
44432          
44433         if (this.resizable) {
44434             this.resizeEl = new Roo.Resizable(this.wrap, {
44435                 pinned : true,
44436                 wrap: true,
44437                 dynamic : true,
44438                 minHeight : this.height,
44439                 height: this.height,
44440                 handles : this.resizable,
44441                 width: this.width,
44442                 listeners : {
44443                     resize : function(r, w, h) {
44444                         _t.onResize(w,h); // -something
44445                     }
44446                 }
44447             });
44448             
44449         }
44450         this.createToolbar(this);
44451        
44452         
44453         if(!this.width){
44454             this.setSize(this.wrap.getSize());
44455         }
44456         if (this.resizeEl) {
44457             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44458             // should trigger onReize..
44459         }
44460         
44461         this.keyNav = new Roo.KeyNav(this.el, {
44462             
44463             "tab" : function(e){
44464                 e.preventDefault();
44465                 
44466                 var value = this.getValue();
44467                 
44468                 var start = this.el.dom.selectionStart;
44469                 var end = this.el.dom.selectionEnd;
44470                 
44471                 if(!e.shiftKey){
44472                     
44473                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44474                     this.el.dom.setSelectionRange(end + 1, end + 1);
44475                     return;
44476                 }
44477                 
44478                 var f = value.substring(0, start).split("\t");
44479                 
44480                 if(f.pop().length != 0){
44481                     return;
44482                 }
44483                 
44484                 this.setValue(f.join("\t") + value.substring(end));
44485                 this.el.dom.setSelectionRange(start - 1, start - 1);
44486                 
44487             },
44488             
44489             "home" : function(e){
44490                 e.preventDefault();
44491                 
44492                 var curr = this.el.dom.selectionStart;
44493                 var lines = this.getValue().split("\n");
44494                 
44495                 if(!lines.length){
44496                     return;
44497                 }
44498                 
44499                 if(e.ctrlKey){
44500                     this.el.dom.setSelectionRange(0, 0);
44501                     return;
44502                 }
44503                 
44504                 var pos = 0;
44505                 
44506                 for (var i = 0; i < lines.length;i++) {
44507                     pos += lines[i].length;
44508                     
44509                     if(i != 0){
44510                         pos += 1;
44511                     }
44512                     
44513                     if(pos < curr){
44514                         continue;
44515                     }
44516                     
44517                     pos -= lines[i].length;
44518                     
44519                     break;
44520                 }
44521                 
44522                 if(!e.shiftKey){
44523                     this.el.dom.setSelectionRange(pos, pos);
44524                     return;
44525                 }
44526                 
44527                 this.el.dom.selectionStart = pos;
44528                 this.el.dom.selectionEnd = curr;
44529             },
44530             
44531             "end" : function(e){
44532                 e.preventDefault();
44533                 
44534                 var curr = this.el.dom.selectionStart;
44535                 var lines = this.getValue().split("\n");
44536                 
44537                 if(!lines.length){
44538                     return;
44539                 }
44540                 
44541                 if(e.ctrlKey){
44542                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44543                     return;
44544                 }
44545                 
44546                 var pos = 0;
44547                 
44548                 for (var i = 0; i < lines.length;i++) {
44549                     
44550                     pos += lines[i].length;
44551                     
44552                     if(i != 0){
44553                         pos += 1;
44554                     }
44555                     
44556                     if(pos < curr){
44557                         continue;
44558                     }
44559                     
44560                     break;
44561                 }
44562                 
44563                 if(!e.shiftKey){
44564                     this.el.dom.setSelectionRange(pos, pos);
44565                     return;
44566                 }
44567                 
44568                 this.el.dom.selectionStart = curr;
44569                 this.el.dom.selectionEnd = pos;
44570             },
44571
44572             scope : this,
44573
44574             doRelay : function(foo, bar, hname){
44575                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44576             },
44577
44578             forceKeyDown: true
44579         });
44580         
44581 //        if(this.autosave && this.w){
44582 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44583 //        }
44584     },
44585
44586     // private
44587     onResize : function(w, h)
44588     {
44589         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44590         var ew = false;
44591         var eh = false;
44592         
44593         if(this.el ){
44594             if(typeof w == 'number'){
44595                 var aw = w - this.wrap.getFrameWidth('lr');
44596                 this.el.setWidth(this.adjustWidth('textarea', aw));
44597                 ew = aw;
44598             }
44599             if(typeof h == 'number'){
44600                 var tbh = 0;
44601                 for (var i =0; i < this.toolbars.length;i++) {
44602                     // fixme - ask toolbars for heights?
44603                     tbh += this.toolbars[i].tb.el.getHeight();
44604                     if (this.toolbars[i].footer) {
44605                         tbh += this.toolbars[i].footer.el.getHeight();
44606                     }
44607                 }
44608                 
44609                 
44610                 
44611                 
44612                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44613                 ah -= 5; // knock a few pixes off for look..
44614 //                Roo.log(ah);
44615                 this.el.setHeight(this.adjustWidth('textarea', ah));
44616                 var eh = ah;
44617             }
44618         }
44619         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44620         this.editorcore.onResize(ew,eh);
44621         
44622     },
44623
44624     /**
44625      * Toggles the editor between standard and source edit mode.
44626      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44627      */
44628     toggleSourceEdit : function(sourceEditMode)
44629     {
44630         this.editorcore.toggleSourceEdit(sourceEditMode);
44631         
44632         if(this.editorcore.sourceEditMode){
44633             Roo.log('editor - showing textarea');
44634             
44635 //            Roo.log('in');
44636 //            Roo.log(this.syncValue());
44637             this.editorcore.syncValue();
44638             this.el.removeClass('x-hidden');
44639             this.el.dom.removeAttribute('tabIndex');
44640             this.el.focus();
44641             
44642             for (var i = 0; i < this.toolbars.length; i++) {
44643                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44644                     this.toolbars[i].tb.hide();
44645                     this.toolbars[i].footer.hide();
44646                 }
44647             }
44648             
44649         }else{
44650             Roo.log('editor - hiding textarea');
44651 //            Roo.log('out')
44652 //            Roo.log(this.pushValue()); 
44653             this.editorcore.pushValue();
44654             
44655             this.el.addClass('x-hidden');
44656             this.el.dom.setAttribute('tabIndex', -1);
44657             
44658             for (var i = 0; i < this.toolbars.length; i++) {
44659                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44660                     this.toolbars[i].tb.show();
44661                     this.toolbars[i].footer.show();
44662                 }
44663             }
44664             
44665             //this.deferFocus();
44666         }
44667         
44668         this.setSize(this.wrap.getSize());
44669         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44670         
44671         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44672     },
44673  
44674     // private (for BoxComponent)
44675     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44676
44677     // private (for BoxComponent)
44678     getResizeEl : function(){
44679         return this.wrap;
44680     },
44681
44682     // private (for BoxComponent)
44683     getPositionEl : function(){
44684         return this.wrap;
44685     },
44686
44687     // private
44688     initEvents : function(){
44689         this.originalValue = this.getValue();
44690     },
44691
44692     /**
44693      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44694      * @method
44695      */
44696     markInvalid : Roo.emptyFn,
44697     /**
44698      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44699      * @method
44700      */
44701     clearInvalid : Roo.emptyFn,
44702
44703     setValue : function(v){
44704         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44705         this.editorcore.pushValue();
44706     },
44707
44708      
44709     // private
44710     deferFocus : function(){
44711         this.focus.defer(10, this);
44712     },
44713
44714     // doc'ed in Field
44715     focus : function(){
44716         this.editorcore.focus();
44717         
44718     },
44719       
44720
44721     // private
44722     onDestroy : function(){
44723         
44724         
44725         
44726         if(this.rendered){
44727             
44728             for (var i =0; i < this.toolbars.length;i++) {
44729                 // fixme - ask toolbars for heights?
44730                 this.toolbars[i].onDestroy();
44731             }
44732             
44733             this.wrap.dom.innerHTML = '';
44734             this.wrap.remove();
44735         }
44736     },
44737
44738     // private
44739     onFirstFocus : function(){
44740         //Roo.log("onFirstFocus");
44741         this.editorcore.onFirstFocus();
44742          for (var i =0; i < this.toolbars.length;i++) {
44743             this.toolbars[i].onFirstFocus();
44744         }
44745         
44746     },
44747     
44748     // private
44749     syncValue : function()
44750     {
44751         this.editorcore.syncValue();
44752     },
44753     
44754     pushValue : function()
44755     {
44756         this.editorcore.pushValue();
44757     },
44758     
44759     setStylesheets : function(stylesheets)
44760     {
44761         this.editorcore.setStylesheets(stylesheets);
44762     },
44763     
44764     removeStylesheets : function()
44765     {
44766         this.editorcore.removeStylesheets();
44767     }
44768      
44769     
44770     // hide stuff that is not compatible
44771     /**
44772      * @event blur
44773      * @hide
44774      */
44775     /**
44776      * @event change
44777      * @hide
44778      */
44779     /**
44780      * @event focus
44781      * @hide
44782      */
44783     /**
44784      * @event specialkey
44785      * @hide
44786      */
44787     /**
44788      * @cfg {String} fieldClass @hide
44789      */
44790     /**
44791      * @cfg {String} focusClass @hide
44792      */
44793     /**
44794      * @cfg {String} autoCreate @hide
44795      */
44796     /**
44797      * @cfg {String} inputType @hide
44798      */
44799     /**
44800      * @cfg {String} invalidClass @hide
44801      */
44802     /**
44803      * @cfg {String} invalidText @hide
44804      */
44805     /**
44806      * @cfg {String} msgFx @hide
44807      */
44808     /**
44809      * @cfg {String} validateOnBlur @hide
44810      */
44811 });
44812  
44813     // <script type="text/javascript">
44814 /*
44815  * Based on
44816  * Ext JS Library 1.1.1
44817  * Copyright(c) 2006-2007, Ext JS, LLC.
44818  *  
44819  
44820  */
44821
44822 /**
44823  * @class Roo.form.HtmlEditorToolbar1
44824  * Basic Toolbar
44825  * 
44826  * Usage:
44827  *
44828  new Roo.form.HtmlEditor({
44829     ....
44830     toolbars : [
44831         new Roo.form.HtmlEditorToolbar1({
44832             disable : { fonts: 1 , format: 1, ..., ... , ...],
44833             btns : [ .... ]
44834         })
44835     }
44836      
44837  * 
44838  * @cfg {Object} disable List of elements to disable..
44839  * @cfg {Array} btns List of additional buttons.
44840  * 
44841  * 
44842  * NEEDS Extra CSS? 
44843  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44844  */
44845  
44846 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44847 {
44848     
44849     Roo.apply(this, config);
44850     
44851     // default disabled, based on 'good practice'..
44852     this.disable = this.disable || {};
44853     Roo.applyIf(this.disable, {
44854         fontSize : true,
44855         colors : true,
44856         specialElements : true
44857     });
44858     
44859     
44860     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44861     // dont call parent... till later.
44862 }
44863
44864 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
44865     
44866     tb: false,
44867     
44868     rendered: false,
44869     
44870     editor : false,
44871     editorcore : false,
44872     /**
44873      * @cfg {Object} disable  List of toolbar elements to disable
44874          
44875      */
44876     disable : false,
44877     
44878     
44879      /**
44880      * @cfg {String} createLinkText The default text for the create link prompt
44881      */
44882     createLinkText : 'Please enter the URL for the link:',
44883     /**
44884      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
44885      */
44886     defaultLinkValue : 'http:/'+'/',
44887    
44888     
44889       /**
44890      * @cfg {Array} fontFamilies An array of available font families
44891      */
44892     fontFamilies : [
44893         'Arial',
44894         'Courier New',
44895         'Tahoma',
44896         'Times New Roman',
44897         'Verdana'
44898     ],
44899     
44900     specialChars : [
44901            "&#169;",
44902           "&#174;",     
44903           "&#8482;",    
44904           "&#163;" ,    
44905          // "&#8212;",    
44906           "&#8230;",    
44907           "&#247;" ,    
44908         //  "&#225;" ,     ?? a acute?
44909            "&#8364;"    , //Euro
44910        //   "&#8220;"    ,
44911         //  "&#8221;"    ,
44912         //  "&#8226;"    ,
44913           "&#176;"  //   , // degrees
44914
44915          // "&#233;"     , // e ecute
44916          // "&#250;"     , // u ecute?
44917     ],
44918     
44919     specialElements : [
44920         {
44921             text: "Insert Table",
44922             xtype: 'MenuItem',
44923             xns : Roo.Menu,
44924             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
44925                 
44926         },
44927         {    
44928             text: "Insert Image",
44929             xtype: 'MenuItem',
44930             xns : Roo.Menu,
44931             ihtml : '<img src="about:blank"/>'
44932             
44933         }
44934         
44935          
44936     ],
44937     
44938     
44939     inputElements : [ 
44940             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
44941             "input:submit", "input:button", "select", "textarea", "label" ],
44942     formats : [
44943         ["p"] ,  
44944         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
44945         ["pre"],[ "code"], 
44946         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
44947         ['div'],['span']
44948     ],
44949     
44950     cleanStyles : [
44951         "font-size"
44952     ],
44953      /**
44954      * @cfg {String} defaultFont default font to use.
44955      */
44956     defaultFont: 'tahoma',
44957    
44958     fontSelect : false,
44959     
44960     
44961     formatCombo : false,
44962     
44963     init : function(editor)
44964     {
44965         this.editor = editor;
44966         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44967         var editorcore = this.editorcore;
44968         
44969         var _t = this;
44970         
44971         var fid = editorcore.frameId;
44972         var etb = this;
44973         function btn(id, toggle, handler){
44974             var xid = fid + '-'+ id ;
44975             return {
44976                 id : xid,
44977                 cmd : id,
44978                 cls : 'x-btn-icon x-edit-'+id,
44979                 enableToggle:toggle !== false,
44980                 scope: _t, // was editor...
44981                 handler:handler||_t.relayBtnCmd,
44982                 clickEvent:'mousedown',
44983                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44984                 tabIndex:-1
44985             };
44986         }
44987         
44988         
44989         
44990         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
44991         this.tb = tb;
44992          // stop form submits
44993         tb.el.on('click', function(e){
44994             e.preventDefault(); // what does this do?
44995         });
44996
44997         if(!this.disable.font) { // && !Roo.isSafari){
44998             /* why no safari for fonts 
44999             editor.fontSelect = tb.el.createChild({
45000                 tag:'select',
45001                 tabIndex: -1,
45002                 cls:'x-font-select',
45003                 html: this.createFontOptions()
45004             });
45005             
45006             editor.fontSelect.on('change', function(){
45007                 var font = editor.fontSelect.dom.value;
45008                 editor.relayCmd('fontname', font);
45009                 editor.deferFocus();
45010             }, editor);
45011             
45012             tb.add(
45013                 editor.fontSelect.dom,
45014                 '-'
45015             );
45016             */
45017             
45018         };
45019         if(!this.disable.formats){
45020             this.formatCombo = new Roo.form.ComboBox({
45021                 store: new Roo.data.SimpleStore({
45022                     id : 'tag',
45023                     fields: ['tag'],
45024                     data : this.formats // from states.js
45025                 }),
45026                 blockFocus : true,
45027                 name : '',
45028                 //autoCreate : {tag: "div",  size: "20"},
45029                 displayField:'tag',
45030                 typeAhead: false,
45031                 mode: 'local',
45032                 editable : false,
45033                 triggerAction: 'all',
45034                 emptyText:'Add tag',
45035                 selectOnFocus:true,
45036                 width:135,
45037                 listeners : {
45038                     'select': function(c, r, i) {
45039                         editorcore.insertTag(r.get('tag'));
45040                         editor.focus();
45041                     }
45042                 }
45043
45044             });
45045             tb.addField(this.formatCombo);
45046             
45047         }
45048         
45049         if(!this.disable.format){
45050             tb.add(
45051                 btn('bold'),
45052                 btn('italic'),
45053                 btn('underline'),
45054                 btn('strikethrough')
45055             );
45056         };
45057         if(!this.disable.fontSize){
45058             tb.add(
45059                 '-',
45060                 
45061                 
45062                 btn('increasefontsize', false, editorcore.adjustFont),
45063                 btn('decreasefontsize', false, editorcore.adjustFont)
45064             );
45065         };
45066         
45067         
45068         if(!this.disable.colors){
45069             tb.add(
45070                 '-', {
45071                     id:editorcore.frameId +'-forecolor',
45072                     cls:'x-btn-icon x-edit-forecolor',
45073                     clickEvent:'mousedown',
45074                     tooltip: this.buttonTips['forecolor'] || undefined,
45075                     tabIndex:-1,
45076                     menu : new Roo.menu.ColorMenu({
45077                         allowReselect: true,
45078                         focus: Roo.emptyFn,
45079                         value:'000000',
45080                         plain:true,
45081                         selectHandler: function(cp, color){
45082                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45083                             editor.deferFocus();
45084                         },
45085                         scope: editorcore,
45086                         clickEvent:'mousedown'
45087                     })
45088                 }, {
45089                     id:editorcore.frameId +'backcolor',
45090                     cls:'x-btn-icon x-edit-backcolor',
45091                     clickEvent:'mousedown',
45092                     tooltip: this.buttonTips['backcolor'] || undefined,
45093                     tabIndex:-1,
45094                     menu : new Roo.menu.ColorMenu({
45095                         focus: Roo.emptyFn,
45096                         value:'FFFFFF',
45097                         plain:true,
45098                         allowReselect: true,
45099                         selectHandler: function(cp, color){
45100                             if(Roo.isGecko){
45101                                 editorcore.execCmd('useCSS', false);
45102                                 editorcore.execCmd('hilitecolor', color);
45103                                 editorcore.execCmd('useCSS', true);
45104                                 editor.deferFocus();
45105                             }else{
45106                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45107                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45108                                 editor.deferFocus();
45109                             }
45110                         },
45111                         scope:editorcore,
45112                         clickEvent:'mousedown'
45113                     })
45114                 }
45115             );
45116         };
45117         // now add all the items...
45118         
45119
45120         if(!this.disable.alignments){
45121             tb.add(
45122                 '-',
45123                 btn('justifyleft'),
45124                 btn('justifycenter'),
45125                 btn('justifyright')
45126             );
45127         };
45128
45129         //if(!Roo.isSafari){
45130             if(!this.disable.links){
45131                 tb.add(
45132                     '-',
45133                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45134                 );
45135             };
45136
45137             if(!this.disable.lists){
45138                 tb.add(
45139                     '-',
45140                     btn('insertorderedlist'),
45141                     btn('insertunorderedlist')
45142                 );
45143             }
45144             if(!this.disable.sourceEdit){
45145                 tb.add(
45146                     '-',
45147                     btn('sourceedit', true, function(btn){
45148                         this.toggleSourceEdit(btn.pressed);
45149                     })
45150                 );
45151             }
45152         //}
45153         
45154         var smenu = { };
45155         // special menu.. - needs to be tidied up..
45156         if (!this.disable.special) {
45157             smenu = {
45158                 text: "&#169;",
45159                 cls: 'x-edit-none',
45160                 
45161                 menu : {
45162                     items : []
45163                 }
45164             };
45165             for (var i =0; i < this.specialChars.length; i++) {
45166                 smenu.menu.items.push({
45167                     
45168                     html: this.specialChars[i],
45169                     handler: function(a,b) {
45170                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45171                         //editor.insertAtCursor(a.html);
45172                         
45173                     },
45174                     tabIndex:-1
45175                 });
45176             }
45177             
45178             
45179             tb.add(smenu);
45180             
45181             
45182         }
45183         
45184         var cmenu = { };
45185         if (!this.disable.cleanStyles) {
45186             cmenu = {
45187                 cls: 'x-btn-icon x-btn-clear',
45188                 
45189                 menu : {
45190                     items : []
45191                 }
45192             };
45193             for (var i =0; i < this.cleanStyles.length; i++) {
45194                 cmenu.menu.items.push({
45195                     actiontype : this.cleanStyles[i],
45196                     html: 'Remove ' + this.cleanStyles[i],
45197                     handler: function(a,b) {
45198 //                        Roo.log(a);
45199 //                        Roo.log(b);
45200                         var c = Roo.get(editorcore.doc.body);
45201                         c.select('[style]').each(function(s) {
45202                             s.dom.style.removeProperty(a.actiontype);
45203                         });
45204                         editorcore.syncValue();
45205                     },
45206                     tabIndex:-1
45207                 });
45208             }
45209              cmenu.menu.items.push({
45210                 actiontype : 'tablewidths',
45211                 html: 'Remove Table Widths',
45212                 handler: function(a,b) {
45213                     editorcore.cleanTableWidths();
45214                     editorcore.syncValue();
45215                 },
45216                 tabIndex:-1
45217             });
45218             cmenu.menu.items.push({
45219                 actiontype : 'word',
45220                 html: 'Remove MS Word Formating',
45221                 handler: function(a,b) {
45222                     editorcore.cleanWord();
45223                     editorcore.syncValue();
45224                 },
45225                 tabIndex:-1
45226             });
45227             
45228             cmenu.menu.items.push({
45229                 actiontype : 'all',
45230                 html: 'Remove All Styles',
45231                 handler: function(a,b) {
45232                     
45233                     var c = Roo.get(editorcore.doc.body);
45234                     c.select('[style]').each(function(s) {
45235                         s.dom.removeAttribute('style');
45236                     });
45237                     editorcore.syncValue();
45238                 },
45239                 tabIndex:-1
45240             });
45241             
45242             cmenu.menu.items.push({
45243                 actiontype : 'all',
45244                 html: 'Remove All CSS Classes',
45245                 handler: function(a,b) {
45246                     
45247                     var c = Roo.get(editorcore.doc.body);
45248                     c.select('[class]').each(function(s) {
45249                         s.dom.className = '';
45250                     });
45251                     editorcore.syncValue();
45252                 },
45253                 tabIndex:-1
45254             });
45255             
45256              cmenu.menu.items.push({
45257                 actiontype : 'tidy',
45258                 html: 'Tidy HTML Source',
45259                 handler: function(a,b) {
45260                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45261                     editorcore.syncValue();
45262                 },
45263                 tabIndex:-1
45264             });
45265             
45266             
45267             tb.add(cmenu);
45268         }
45269          
45270         if (!this.disable.specialElements) {
45271             var semenu = {
45272                 text: "Other;",
45273                 cls: 'x-edit-none',
45274                 menu : {
45275                     items : []
45276                 }
45277             };
45278             for (var i =0; i < this.specialElements.length; i++) {
45279                 semenu.menu.items.push(
45280                     Roo.apply({ 
45281                         handler: function(a,b) {
45282                             editor.insertAtCursor(this.ihtml);
45283                         }
45284                     }, this.specialElements[i])
45285                 );
45286                     
45287             }
45288             
45289             tb.add(semenu);
45290             
45291             
45292         }
45293          
45294         
45295         if (this.btns) {
45296             for(var i =0; i< this.btns.length;i++) {
45297                 var b = Roo.factory(this.btns[i],Roo.form);
45298                 b.cls =  'x-edit-none';
45299                 
45300                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45301                     b.cls += ' x-init-enable';
45302                 }
45303                 
45304                 b.scope = editorcore;
45305                 tb.add(b);
45306             }
45307         
45308         }
45309         
45310         
45311         
45312         // disable everything...
45313         
45314         this.tb.items.each(function(item){
45315             
45316            if(
45317                 item.id != editorcore.frameId+ '-sourceedit' && 
45318                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45319             ){
45320                 
45321                 item.disable();
45322             }
45323         });
45324         this.rendered = true;
45325         
45326         // the all the btns;
45327         editor.on('editorevent', this.updateToolbar, this);
45328         // other toolbars need to implement this..
45329         //editor.on('editmodechange', this.updateToolbar, this);
45330     },
45331     
45332     
45333     relayBtnCmd : function(btn) {
45334         this.editorcore.relayCmd(btn.cmd);
45335     },
45336     // private used internally
45337     createLink : function(){
45338         Roo.log("create link?");
45339         var url = prompt(this.createLinkText, this.defaultLinkValue);
45340         if(url && url != 'http:/'+'/'){
45341             this.editorcore.relayCmd('createlink', url);
45342         }
45343     },
45344
45345     
45346     /**
45347      * Protected method that will not generally be called directly. It triggers
45348      * a toolbar update by reading the markup state of the current selection in the editor.
45349      */
45350     updateToolbar: function(){
45351
45352         if(!this.editorcore.activated){
45353             this.editor.onFirstFocus();
45354             return;
45355         }
45356
45357         var btns = this.tb.items.map, 
45358             doc = this.editorcore.doc,
45359             frameId = this.editorcore.frameId;
45360
45361         if(!this.disable.font && !Roo.isSafari){
45362             /*
45363             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45364             if(name != this.fontSelect.dom.value){
45365                 this.fontSelect.dom.value = name;
45366             }
45367             */
45368         }
45369         if(!this.disable.format){
45370             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45371             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45372             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45373             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45374         }
45375         if(!this.disable.alignments){
45376             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45377             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45378             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45379         }
45380         if(!Roo.isSafari && !this.disable.lists){
45381             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45382             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45383         }
45384         
45385         var ans = this.editorcore.getAllAncestors();
45386         if (this.formatCombo) {
45387             
45388             
45389             var store = this.formatCombo.store;
45390             this.formatCombo.setValue("");
45391             for (var i =0; i < ans.length;i++) {
45392                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45393                     // select it..
45394                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45395                     break;
45396                 }
45397             }
45398         }
45399         
45400         
45401         
45402         // hides menus... - so this cant be on a menu...
45403         Roo.menu.MenuMgr.hideAll();
45404
45405         //this.editorsyncValue();
45406     },
45407    
45408     
45409     createFontOptions : function(){
45410         var buf = [], fs = this.fontFamilies, ff, lc;
45411         
45412         
45413         
45414         for(var i = 0, len = fs.length; i< len; i++){
45415             ff = fs[i];
45416             lc = ff.toLowerCase();
45417             buf.push(
45418                 '<option value="',lc,'" style="font-family:',ff,';"',
45419                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45420                     ff,
45421                 '</option>'
45422             );
45423         }
45424         return buf.join('');
45425     },
45426     
45427     toggleSourceEdit : function(sourceEditMode){
45428         
45429         Roo.log("toolbar toogle");
45430         if(sourceEditMode === undefined){
45431             sourceEditMode = !this.sourceEditMode;
45432         }
45433         this.sourceEditMode = sourceEditMode === true;
45434         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45435         // just toggle the button?
45436         if(btn.pressed !== this.sourceEditMode){
45437             btn.toggle(this.sourceEditMode);
45438             return;
45439         }
45440         
45441         if(sourceEditMode){
45442             Roo.log("disabling buttons");
45443             this.tb.items.each(function(item){
45444                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45445                     item.disable();
45446                 }
45447             });
45448           
45449         }else{
45450             Roo.log("enabling buttons");
45451             if(this.editorcore.initialized){
45452                 this.tb.items.each(function(item){
45453                     item.enable();
45454                 });
45455             }
45456             
45457         }
45458         Roo.log("calling toggole on editor");
45459         // tell the editor that it's been pressed..
45460         this.editor.toggleSourceEdit(sourceEditMode);
45461        
45462     },
45463      /**
45464      * Object collection of toolbar tooltips for the buttons in the editor. The key
45465      * is the command id associated with that button and the value is a valid QuickTips object.
45466      * For example:
45467 <pre><code>
45468 {
45469     bold : {
45470         title: 'Bold (Ctrl+B)',
45471         text: 'Make the selected text bold.',
45472         cls: 'x-html-editor-tip'
45473     },
45474     italic : {
45475         title: 'Italic (Ctrl+I)',
45476         text: 'Make the selected text italic.',
45477         cls: 'x-html-editor-tip'
45478     },
45479     ...
45480 </code></pre>
45481     * @type Object
45482      */
45483     buttonTips : {
45484         bold : {
45485             title: 'Bold (Ctrl+B)',
45486             text: 'Make the selected text bold.',
45487             cls: 'x-html-editor-tip'
45488         },
45489         italic : {
45490             title: 'Italic (Ctrl+I)',
45491             text: 'Make the selected text italic.',
45492             cls: 'x-html-editor-tip'
45493         },
45494         underline : {
45495             title: 'Underline (Ctrl+U)',
45496             text: 'Underline the selected text.',
45497             cls: 'x-html-editor-tip'
45498         },
45499         strikethrough : {
45500             title: 'Strikethrough',
45501             text: 'Strikethrough the selected text.',
45502             cls: 'x-html-editor-tip'
45503         },
45504         increasefontsize : {
45505             title: 'Grow Text',
45506             text: 'Increase the font size.',
45507             cls: 'x-html-editor-tip'
45508         },
45509         decreasefontsize : {
45510             title: 'Shrink Text',
45511             text: 'Decrease the font size.',
45512             cls: 'x-html-editor-tip'
45513         },
45514         backcolor : {
45515             title: 'Text Highlight Color',
45516             text: 'Change the background color of the selected text.',
45517             cls: 'x-html-editor-tip'
45518         },
45519         forecolor : {
45520             title: 'Font Color',
45521             text: 'Change the color of the selected text.',
45522             cls: 'x-html-editor-tip'
45523         },
45524         justifyleft : {
45525             title: 'Align Text Left',
45526             text: 'Align text to the left.',
45527             cls: 'x-html-editor-tip'
45528         },
45529         justifycenter : {
45530             title: 'Center Text',
45531             text: 'Center text in the editor.',
45532             cls: 'x-html-editor-tip'
45533         },
45534         justifyright : {
45535             title: 'Align Text Right',
45536             text: 'Align text to the right.',
45537             cls: 'x-html-editor-tip'
45538         },
45539         insertunorderedlist : {
45540             title: 'Bullet List',
45541             text: 'Start a bulleted list.',
45542             cls: 'x-html-editor-tip'
45543         },
45544         insertorderedlist : {
45545             title: 'Numbered List',
45546             text: 'Start a numbered list.',
45547             cls: 'x-html-editor-tip'
45548         },
45549         createlink : {
45550             title: 'Hyperlink',
45551             text: 'Make the selected text a hyperlink.',
45552             cls: 'x-html-editor-tip'
45553         },
45554         sourceedit : {
45555             title: 'Source Edit',
45556             text: 'Switch to source editing mode.',
45557             cls: 'x-html-editor-tip'
45558         }
45559     },
45560     // private
45561     onDestroy : function(){
45562         if(this.rendered){
45563             
45564             this.tb.items.each(function(item){
45565                 if(item.menu){
45566                     item.menu.removeAll();
45567                     if(item.menu.el){
45568                         item.menu.el.destroy();
45569                     }
45570                 }
45571                 item.destroy();
45572             });
45573              
45574         }
45575     },
45576     onFirstFocus: function() {
45577         this.tb.items.each(function(item){
45578            item.enable();
45579         });
45580     }
45581 });
45582
45583
45584
45585
45586 // <script type="text/javascript">
45587 /*
45588  * Based on
45589  * Ext JS Library 1.1.1
45590  * Copyright(c) 2006-2007, Ext JS, LLC.
45591  *  
45592  
45593  */
45594
45595  
45596 /**
45597  * @class Roo.form.HtmlEditor.ToolbarContext
45598  * Context Toolbar
45599  * 
45600  * Usage:
45601  *
45602  new Roo.form.HtmlEditor({
45603     ....
45604     toolbars : [
45605         { xtype: 'ToolbarStandard', styles : {} }
45606         { xtype: 'ToolbarContext', disable : {} }
45607     ]
45608 })
45609
45610      
45611  * 
45612  * @config : {Object} disable List of elements to disable.. (not done yet.)
45613  * @config : {Object} styles  Map of styles available.
45614  * 
45615  */
45616
45617 Roo.form.HtmlEditor.ToolbarContext = function(config)
45618 {
45619     
45620     Roo.apply(this, config);
45621     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45622     // dont call parent... till later.
45623     this.styles = this.styles || {};
45624 }
45625
45626  
45627
45628 Roo.form.HtmlEditor.ToolbarContext.types = {
45629     'IMG' : {
45630         width : {
45631             title: "Width",
45632             width: 40
45633         },
45634         height:  {
45635             title: "Height",
45636             width: 40
45637         },
45638         align: {
45639             title: "Align",
45640             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45641             width : 80
45642             
45643         },
45644         border: {
45645             title: "Border",
45646             width: 40
45647         },
45648         alt: {
45649             title: "Alt",
45650             width: 120
45651         },
45652         src : {
45653             title: "Src",
45654             width: 220
45655         }
45656         
45657     },
45658     'A' : {
45659         name : {
45660             title: "Name",
45661             width: 50
45662         },
45663         target:  {
45664             title: "Target",
45665             width: 120
45666         },
45667         href:  {
45668             title: "Href",
45669             width: 220
45670         } // border?
45671         
45672     },
45673     'TABLE' : {
45674         rows : {
45675             title: "Rows",
45676             width: 20
45677         },
45678         cols : {
45679             title: "Cols",
45680             width: 20
45681         },
45682         width : {
45683             title: "Width",
45684             width: 40
45685         },
45686         height : {
45687             title: "Height",
45688             width: 40
45689         },
45690         border : {
45691             title: "Border",
45692             width: 20
45693         }
45694     },
45695     'TD' : {
45696         width : {
45697             title: "Width",
45698             width: 40
45699         },
45700         height : {
45701             title: "Height",
45702             width: 40
45703         },   
45704         align: {
45705             title: "Align",
45706             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45707             width: 80
45708         },
45709         valign: {
45710             title: "Valign",
45711             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45712             width: 80
45713         },
45714         colspan: {
45715             title: "Colspan",
45716             width: 20
45717             
45718         },
45719          'font-family'  : {
45720             title : "Font",
45721             style : 'fontFamily',
45722             displayField: 'display',
45723             optname : 'font-family',
45724             width: 140
45725         }
45726     },
45727     'INPUT' : {
45728         name : {
45729             title: "name",
45730             width: 120
45731         },
45732         value : {
45733             title: "Value",
45734             width: 120
45735         },
45736         width : {
45737             title: "Width",
45738             width: 40
45739         }
45740     },
45741     'LABEL' : {
45742         'for' : {
45743             title: "For",
45744             width: 120
45745         }
45746     },
45747     'TEXTAREA' : {
45748           name : {
45749             title: "name",
45750             width: 120
45751         },
45752         rows : {
45753             title: "Rows",
45754             width: 20
45755         },
45756         cols : {
45757             title: "Cols",
45758             width: 20
45759         }
45760     },
45761     'SELECT' : {
45762         name : {
45763             title: "name",
45764             width: 120
45765         },
45766         selectoptions : {
45767             title: "Options",
45768             width: 200
45769         }
45770     },
45771     
45772     // should we really allow this??
45773     // should this just be 
45774     'BODY' : {
45775         title : {
45776             title: "Title",
45777             width: 200,
45778             disabled : true
45779         }
45780     },
45781     'SPAN' : {
45782         'font-family'  : {
45783             title : "Font",
45784             style : 'fontFamily',
45785             displayField: 'display',
45786             optname : 'font-family',
45787             width: 140
45788         }
45789     },
45790     'DIV' : {
45791         'font-family'  : {
45792             title : "Font",
45793             style : 'fontFamily',
45794             displayField: 'display',
45795             optname : 'font-family',
45796             width: 140
45797         }
45798     },
45799      'P' : {
45800         'font-family'  : {
45801             title : "Font",
45802             style : 'fontFamily',
45803             displayField: 'display',
45804             optname : 'font-family',
45805             width: 140
45806         }
45807     },
45808     
45809     '*' : {
45810         // empty..
45811     }
45812
45813 };
45814
45815 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45816 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45817
45818 Roo.form.HtmlEditor.ToolbarContext.options = {
45819         'font-family'  : [ 
45820                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45821                 [ 'Courier New', 'Courier New'],
45822                 [ 'Tahoma', 'Tahoma'],
45823                 [ 'Times New Roman,serif', 'Times'],
45824                 [ 'Verdana','Verdana' ]
45825         ]
45826 };
45827
45828 // fixme - these need to be configurable..
45829  
45830
45831 //Roo.form.HtmlEditor.ToolbarContext.types
45832
45833
45834 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45835     
45836     tb: false,
45837     
45838     rendered: false,
45839     
45840     editor : false,
45841     editorcore : false,
45842     /**
45843      * @cfg {Object} disable  List of toolbar elements to disable
45844          
45845      */
45846     disable : false,
45847     /**
45848      * @cfg {Object} styles List of styles 
45849      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45850      *
45851      * These must be defined in the page, so they get rendered correctly..
45852      * .headline { }
45853      * TD.underline { }
45854      * 
45855      */
45856     styles : false,
45857     
45858     options: false,
45859     
45860     toolbars : false,
45861     
45862     init : function(editor)
45863     {
45864         this.editor = editor;
45865         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45866         var editorcore = this.editorcore;
45867         
45868         var fid = editorcore.frameId;
45869         var etb = this;
45870         function btn(id, toggle, handler){
45871             var xid = fid + '-'+ id ;
45872             return {
45873                 id : xid,
45874                 cmd : id,
45875                 cls : 'x-btn-icon x-edit-'+id,
45876                 enableToggle:toggle !== false,
45877                 scope: editorcore, // was editor...
45878                 handler:handler||editorcore.relayBtnCmd,
45879                 clickEvent:'mousedown',
45880                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45881                 tabIndex:-1
45882             };
45883         }
45884         // create a new element.
45885         var wdiv = editor.wrap.createChild({
45886                 tag: 'div'
45887             }, editor.wrap.dom.firstChild.nextSibling, true);
45888         
45889         // can we do this more than once??
45890         
45891          // stop form submits
45892       
45893  
45894         // disable everything...
45895         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
45896         this.toolbars = {};
45897            
45898         for (var i in  ty) {
45899           
45900             this.toolbars[i] = this.buildToolbar(ty[i],i);
45901         }
45902         this.tb = this.toolbars.BODY;
45903         this.tb.el.show();
45904         this.buildFooter();
45905         this.footer.show();
45906         editor.on('hide', function( ) { this.footer.hide() }, this);
45907         editor.on('show', function( ) { this.footer.show() }, this);
45908         
45909          
45910         this.rendered = true;
45911         
45912         // the all the btns;
45913         editor.on('editorevent', this.updateToolbar, this);
45914         // other toolbars need to implement this..
45915         //editor.on('editmodechange', this.updateToolbar, this);
45916     },
45917     
45918     
45919     
45920     /**
45921      * Protected method that will not generally be called directly. It triggers
45922      * a toolbar update by reading the markup state of the current selection in the editor.
45923      *
45924      * Note you can force an update by calling on('editorevent', scope, false)
45925      */
45926     updateToolbar: function(editor,ev,sel){
45927
45928         //Roo.log(ev);
45929         // capture mouse up - this is handy for selecting images..
45930         // perhaps should go somewhere else...
45931         if(!this.editorcore.activated){
45932              this.editor.onFirstFocus();
45933             return;
45934         }
45935         
45936         
45937         
45938         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
45939         // selectNode - might want to handle IE?
45940         if (ev &&
45941             (ev.type == 'mouseup' || ev.type == 'click' ) &&
45942             ev.target && ev.target.tagName == 'IMG') {
45943             // they have click on an image...
45944             // let's see if we can change the selection...
45945             sel = ev.target;
45946          
45947               var nodeRange = sel.ownerDocument.createRange();
45948             try {
45949                 nodeRange.selectNode(sel);
45950             } catch (e) {
45951                 nodeRange.selectNodeContents(sel);
45952             }
45953             //nodeRange.collapse(true);
45954             var s = this.editorcore.win.getSelection();
45955             s.removeAllRanges();
45956             s.addRange(nodeRange);
45957         }  
45958         
45959       
45960         var updateFooter = sel ? false : true;
45961         
45962         
45963         var ans = this.editorcore.getAllAncestors();
45964         
45965         // pick
45966         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
45967         
45968         if (!sel) { 
45969             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
45970             sel = sel ? sel : this.editorcore.doc.body;
45971             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
45972             
45973         }
45974         // pick a menu that exists..
45975         var tn = sel.tagName.toUpperCase();
45976         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
45977         
45978         tn = sel.tagName.toUpperCase();
45979         
45980         var lastSel = this.tb.selectedNode;
45981         
45982         this.tb.selectedNode = sel;
45983         
45984         // if current menu does not match..
45985         
45986         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
45987                 
45988             this.tb.el.hide();
45989             ///console.log("show: " + tn);
45990             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
45991             this.tb.el.show();
45992             // update name
45993             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
45994             
45995             
45996             // update attributes
45997             if (this.tb.fields) {
45998                 this.tb.fields.each(function(e) {
45999                     if (e.stylename) {
46000                         e.setValue(sel.style[e.stylename]);
46001                         return;
46002                     } 
46003                    e.setValue(sel.getAttribute(e.attrname));
46004                 });
46005             }
46006             
46007             var hasStyles = false;
46008             for(var i in this.styles) {
46009                 hasStyles = true;
46010                 break;
46011             }
46012             
46013             // update styles
46014             if (hasStyles) { 
46015                 var st = this.tb.fields.item(0);
46016                 
46017                 st.store.removeAll();
46018                
46019                 
46020                 var cn = sel.className.split(/\s+/);
46021                 
46022                 var avs = [];
46023                 if (this.styles['*']) {
46024                     
46025                     Roo.each(this.styles['*'], function(v) {
46026                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46027                     });
46028                 }
46029                 if (this.styles[tn]) { 
46030                     Roo.each(this.styles[tn], function(v) {
46031                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46032                     });
46033                 }
46034                 
46035                 st.store.loadData(avs);
46036                 st.collapse();
46037                 st.setValue(cn);
46038             }
46039             // flag our selected Node.
46040             this.tb.selectedNode = sel;
46041            
46042            
46043             Roo.menu.MenuMgr.hideAll();
46044
46045         }
46046         
46047         if (!updateFooter) {
46048             //this.footDisp.dom.innerHTML = ''; 
46049             return;
46050         }
46051         // update the footer
46052         //
46053         var html = '';
46054         
46055         this.footerEls = ans.reverse();
46056         Roo.each(this.footerEls, function(a,i) {
46057             if (!a) { return; }
46058             html += html.length ? ' &gt; '  :  '';
46059             
46060             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46061             
46062         });
46063        
46064         // 
46065         var sz = this.footDisp.up('td').getSize();
46066         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46067         this.footDisp.dom.style.marginLeft = '5px';
46068         
46069         this.footDisp.dom.style.overflow = 'hidden';
46070         
46071         this.footDisp.dom.innerHTML = html;
46072             
46073         //this.editorsyncValue();
46074     },
46075      
46076     
46077    
46078        
46079     // private
46080     onDestroy : function(){
46081         if(this.rendered){
46082             
46083             this.tb.items.each(function(item){
46084                 if(item.menu){
46085                     item.menu.removeAll();
46086                     if(item.menu.el){
46087                         item.menu.el.destroy();
46088                     }
46089                 }
46090                 item.destroy();
46091             });
46092              
46093         }
46094     },
46095     onFirstFocus: function() {
46096         // need to do this for all the toolbars..
46097         this.tb.items.each(function(item){
46098            item.enable();
46099         });
46100     },
46101     buildToolbar: function(tlist, nm)
46102     {
46103         var editor = this.editor;
46104         var editorcore = this.editorcore;
46105          // create a new element.
46106         var wdiv = editor.wrap.createChild({
46107                 tag: 'div'
46108             }, editor.wrap.dom.firstChild.nextSibling, true);
46109         
46110        
46111         var tb = new Roo.Toolbar(wdiv);
46112         // add the name..
46113         
46114         tb.add(nm+ ":&nbsp;");
46115         
46116         var styles = [];
46117         for(var i in this.styles) {
46118             styles.push(i);
46119         }
46120         
46121         // styles...
46122         if (styles && styles.length) {
46123             
46124             // this needs a multi-select checkbox...
46125             tb.addField( new Roo.form.ComboBox({
46126                 store: new Roo.data.SimpleStore({
46127                     id : 'val',
46128                     fields: ['val', 'selected'],
46129                     data : [] 
46130                 }),
46131                 name : '-roo-edit-className',
46132                 attrname : 'className',
46133                 displayField: 'val',
46134                 typeAhead: false,
46135                 mode: 'local',
46136                 editable : false,
46137                 triggerAction: 'all',
46138                 emptyText:'Select Style',
46139                 selectOnFocus:true,
46140                 width: 130,
46141                 listeners : {
46142                     'select': function(c, r, i) {
46143                         // initial support only for on class per el..
46144                         tb.selectedNode.className =  r ? r.get('val') : '';
46145                         editorcore.syncValue();
46146                     }
46147                 }
46148     
46149             }));
46150         }
46151         
46152         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46153         var tbops = tbc.options;
46154         
46155         for (var i in tlist) {
46156             
46157             var item = tlist[i];
46158             tb.add(item.title + ":&nbsp;");
46159             
46160             
46161             //optname == used so you can configure the options available..
46162             var opts = item.opts ? item.opts : false;
46163             if (item.optname) {
46164                 opts = tbops[item.optname];
46165            
46166             }
46167             
46168             if (opts) {
46169                 // opts == pulldown..
46170                 tb.addField( new Roo.form.ComboBox({
46171                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46172                         id : 'val',
46173                         fields: ['val', 'display'],
46174                         data : opts  
46175                     }),
46176                     name : '-roo-edit-' + i,
46177                     attrname : i,
46178                     stylename : item.style ? item.style : false,
46179                     displayField: item.displayField ? item.displayField : 'val',
46180                     valueField :  'val',
46181                     typeAhead: false,
46182                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46183                     editable : false,
46184                     triggerAction: 'all',
46185                     emptyText:'Select',
46186                     selectOnFocus:true,
46187                     width: item.width ? item.width  : 130,
46188                     listeners : {
46189                         'select': function(c, r, i) {
46190                             if (c.stylename) {
46191                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46192                                 return;
46193                             }
46194                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46195                         }
46196                     }
46197
46198                 }));
46199                 continue;
46200                     
46201                  
46202                 
46203                 tb.addField( new Roo.form.TextField({
46204                     name: i,
46205                     width: 100,
46206                     //allowBlank:false,
46207                     value: ''
46208                 }));
46209                 continue;
46210             }
46211             tb.addField( new Roo.form.TextField({
46212                 name: '-roo-edit-' + i,
46213                 attrname : i,
46214                 
46215                 width: item.width,
46216                 //allowBlank:true,
46217                 value: '',
46218                 listeners: {
46219                     'change' : function(f, nv, ov) {
46220                         tb.selectedNode.setAttribute(f.attrname, nv);
46221                         editorcore.syncValue();
46222                     }
46223                 }
46224             }));
46225              
46226         }
46227         
46228         var _this = this;
46229         
46230         if(nm == 'BODY'){
46231             tb.addSeparator();
46232         
46233             tb.addButton( {
46234                 text: 'Stylesheets',
46235
46236                 listeners : {
46237                     click : function ()
46238                     {
46239                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46240                     }
46241                 }
46242             });
46243         }
46244         
46245         tb.addFill();
46246         tb.addButton( {
46247             text: 'Remove Tag',
46248     
46249             listeners : {
46250                 click : function ()
46251                 {
46252                     // remove
46253                     // undo does not work.
46254                      
46255                     var sn = tb.selectedNode;
46256                     
46257                     var pn = sn.parentNode;
46258                     
46259                     var stn =  sn.childNodes[0];
46260                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46261                     while (sn.childNodes.length) {
46262                         var node = sn.childNodes[0];
46263                         sn.removeChild(node);
46264                         //Roo.log(node);
46265                         pn.insertBefore(node, sn);
46266                         
46267                     }
46268                     pn.removeChild(sn);
46269                     var range = editorcore.createRange();
46270         
46271                     range.setStart(stn,0);
46272                     range.setEnd(en,0); //????
46273                     //range.selectNode(sel);
46274                     
46275                     
46276                     var selection = editorcore.getSelection();
46277                     selection.removeAllRanges();
46278                     selection.addRange(range);
46279                     
46280                     
46281                     
46282                     //_this.updateToolbar(null, null, pn);
46283                     _this.updateToolbar(null, null, null);
46284                     _this.footDisp.dom.innerHTML = ''; 
46285                 }
46286             }
46287             
46288                     
46289                 
46290             
46291         });
46292         
46293         
46294         tb.el.on('click', function(e){
46295             e.preventDefault(); // what does this do?
46296         });
46297         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46298         tb.el.hide();
46299         tb.name = nm;
46300         // dont need to disable them... as they will get hidden
46301         return tb;
46302          
46303         
46304     },
46305     buildFooter : function()
46306     {
46307         
46308         var fel = this.editor.wrap.createChild();
46309         this.footer = new Roo.Toolbar(fel);
46310         // toolbar has scrolly on left / right?
46311         var footDisp= new Roo.Toolbar.Fill();
46312         var _t = this;
46313         this.footer.add(
46314             {
46315                 text : '&lt;',
46316                 xtype: 'Button',
46317                 handler : function() {
46318                     _t.footDisp.scrollTo('left',0,true)
46319                 }
46320             }
46321         );
46322         this.footer.add( footDisp );
46323         this.footer.add( 
46324             {
46325                 text : '&gt;',
46326                 xtype: 'Button',
46327                 handler : function() {
46328                     // no animation..
46329                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46330                 }
46331             }
46332         );
46333         var fel = Roo.get(footDisp.el);
46334         fel.addClass('x-editor-context');
46335         this.footDispWrap = fel; 
46336         this.footDispWrap.overflow  = 'hidden';
46337         
46338         this.footDisp = fel.createChild();
46339         this.footDispWrap.on('click', this.onContextClick, this)
46340         
46341         
46342     },
46343     onContextClick : function (ev,dom)
46344     {
46345         ev.preventDefault();
46346         var  cn = dom.className;
46347         //Roo.log(cn);
46348         if (!cn.match(/x-ed-loc-/)) {
46349             return;
46350         }
46351         var n = cn.split('-').pop();
46352         var ans = this.footerEls;
46353         var sel = ans[n];
46354         
46355          // pick
46356         var range = this.editorcore.createRange();
46357         
46358         range.selectNodeContents(sel);
46359         //range.selectNode(sel);
46360         
46361         
46362         var selection = this.editorcore.getSelection();
46363         selection.removeAllRanges();
46364         selection.addRange(range);
46365         
46366         
46367         
46368         this.updateToolbar(null, null, sel);
46369         
46370         
46371     }
46372     
46373     
46374     
46375     
46376     
46377 });
46378
46379
46380
46381
46382
46383 /*
46384  * Based on:
46385  * Ext JS Library 1.1.1
46386  * Copyright(c) 2006-2007, Ext JS, LLC.
46387  *
46388  * Originally Released Under LGPL - original licence link has changed is not relivant.
46389  *
46390  * Fork - LGPL
46391  * <script type="text/javascript">
46392  */
46393  
46394 /**
46395  * @class Roo.form.BasicForm
46396  * @extends Roo.util.Observable
46397  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46398  * @constructor
46399  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46400  * @param {Object} config Configuration options
46401  */
46402 Roo.form.BasicForm = function(el, config){
46403     this.allItems = [];
46404     this.childForms = [];
46405     Roo.apply(this, config);
46406     /*
46407      * The Roo.form.Field items in this form.
46408      * @type MixedCollection
46409      */
46410      
46411      
46412     this.items = new Roo.util.MixedCollection(false, function(o){
46413         return o.id || (o.id = Roo.id());
46414     });
46415     this.addEvents({
46416         /**
46417          * @event beforeaction
46418          * Fires before any action is performed. Return false to cancel the action.
46419          * @param {Form} this
46420          * @param {Action} action The action to be performed
46421          */
46422         beforeaction: true,
46423         /**
46424          * @event actionfailed
46425          * Fires when an action fails.
46426          * @param {Form} this
46427          * @param {Action} action The action that failed
46428          */
46429         actionfailed : true,
46430         /**
46431          * @event actioncomplete
46432          * Fires when an action is completed.
46433          * @param {Form} this
46434          * @param {Action} action The action that completed
46435          */
46436         actioncomplete : true
46437     });
46438     if(el){
46439         this.initEl(el);
46440     }
46441     Roo.form.BasicForm.superclass.constructor.call(this);
46442 };
46443
46444 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46445     /**
46446      * @cfg {String} method
46447      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46448      */
46449     /**
46450      * @cfg {DataReader} reader
46451      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46452      * This is optional as there is built-in support for processing JSON.
46453      */
46454     /**
46455      * @cfg {DataReader} errorReader
46456      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46457      * This is completely optional as there is built-in support for processing JSON.
46458      */
46459     /**
46460      * @cfg {String} url
46461      * The URL to use for form actions if one isn't supplied in the action options.
46462      */
46463     /**
46464      * @cfg {Boolean} fileUpload
46465      * Set to true if this form is a file upload.
46466      */
46467      
46468     /**
46469      * @cfg {Object} baseParams
46470      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46471      */
46472      /**
46473      
46474     /**
46475      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46476      */
46477     timeout: 30,
46478
46479     // private
46480     activeAction : null,
46481
46482     /**
46483      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46484      * or setValues() data instead of when the form was first created.
46485      */
46486     trackResetOnLoad : false,
46487     
46488     
46489     /**
46490      * childForms - used for multi-tab forms
46491      * @type {Array}
46492      */
46493     childForms : false,
46494     
46495     /**
46496      * allItems - full list of fields.
46497      * @type {Array}
46498      */
46499     allItems : false,
46500     
46501     /**
46502      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46503      * element by passing it or its id or mask the form itself by passing in true.
46504      * @type Mixed
46505      */
46506     waitMsgTarget : false,
46507
46508     // private
46509     initEl : function(el){
46510         this.el = Roo.get(el);
46511         this.id = this.el.id || Roo.id();
46512         this.el.on('submit', this.onSubmit, this);
46513         this.el.addClass('x-form');
46514     },
46515
46516     // private
46517     onSubmit : function(e){
46518         e.stopEvent();
46519     },
46520
46521     /**
46522      * Returns true if client-side validation on the form is successful.
46523      * @return Boolean
46524      */
46525     isValid : function(){
46526         var valid = true;
46527         this.items.each(function(f){
46528            if(!f.validate()){
46529                valid = false;
46530            }
46531         });
46532         return valid;
46533     },
46534
46535     /**
46536      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46537      * @return Boolean
46538      */
46539     isDirty : function(){
46540         var dirty = false;
46541         this.items.each(function(f){
46542            if(f.isDirty()){
46543                dirty = true;
46544                return false;
46545            }
46546         });
46547         return dirty;
46548     },
46549     
46550     /**
46551      * Returns true if any fields in this form have changed since their original load. (New version)
46552      * @return Boolean
46553      */
46554     
46555     hasChanged : function()
46556     {
46557         var dirty = false;
46558         this.items.each(function(f){
46559            if(f.hasChanged()){
46560                dirty = true;
46561                return false;
46562            }
46563         });
46564         return dirty;
46565         
46566     },
46567     /**
46568      * Resets all hasChanged to 'false' -
46569      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46570      * So hasChanged storage is only to be used for this purpose
46571      * @return Boolean
46572      */
46573     resetHasChanged : function()
46574     {
46575         this.items.each(function(f){
46576            f.resetHasChanged();
46577         });
46578         
46579     },
46580     
46581     
46582     /**
46583      * Performs a predefined action (submit or load) or custom actions you define on this form.
46584      * @param {String} actionName The name of the action type
46585      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46586      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46587      * accept other config options):
46588      * <pre>
46589 Property          Type             Description
46590 ----------------  ---------------  ----------------------------------------------------------------------------------
46591 url               String           The url for the action (defaults to the form's url)
46592 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46593 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46594 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46595                                    validate the form on the client (defaults to false)
46596      * </pre>
46597      * @return {BasicForm} this
46598      */
46599     doAction : function(action, options){
46600         if(typeof action == 'string'){
46601             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46602         }
46603         if(this.fireEvent('beforeaction', this, action) !== false){
46604             this.beforeAction(action);
46605             action.run.defer(100, action);
46606         }
46607         return this;
46608     },
46609
46610     /**
46611      * Shortcut to do a submit action.
46612      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46613      * @return {BasicForm} this
46614      */
46615     submit : function(options){
46616         this.doAction('submit', options);
46617         return this;
46618     },
46619
46620     /**
46621      * Shortcut to do a load action.
46622      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46623      * @return {BasicForm} this
46624      */
46625     load : function(options){
46626         this.doAction('load', options);
46627         return this;
46628     },
46629
46630     /**
46631      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46632      * @param {Record} record The record to edit
46633      * @return {BasicForm} this
46634      */
46635     updateRecord : function(record){
46636         record.beginEdit();
46637         var fs = record.fields;
46638         fs.each(function(f){
46639             var field = this.findField(f.name);
46640             if(field){
46641                 record.set(f.name, field.getValue());
46642             }
46643         }, this);
46644         record.endEdit();
46645         return this;
46646     },
46647
46648     /**
46649      * Loads an Roo.data.Record into this form.
46650      * @param {Record} record The record to load
46651      * @return {BasicForm} this
46652      */
46653     loadRecord : function(record){
46654         this.setValues(record.data);
46655         return this;
46656     },
46657
46658     // private
46659     beforeAction : function(action){
46660         var o = action.options;
46661         
46662        
46663         if(this.waitMsgTarget === true){
46664             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46665         }else if(this.waitMsgTarget){
46666             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46667             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46668         }else {
46669             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46670         }
46671          
46672     },
46673
46674     // private
46675     afterAction : function(action, success){
46676         this.activeAction = null;
46677         var o = action.options;
46678         
46679         if(this.waitMsgTarget === true){
46680             this.el.unmask();
46681         }else if(this.waitMsgTarget){
46682             this.waitMsgTarget.unmask();
46683         }else{
46684             Roo.MessageBox.updateProgress(1);
46685             Roo.MessageBox.hide();
46686         }
46687          
46688         if(success){
46689             if(o.reset){
46690                 this.reset();
46691             }
46692             Roo.callback(o.success, o.scope, [this, action]);
46693             this.fireEvent('actioncomplete', this, action);
46694             
46695         }else{
46696             
46697             // failure condition..
46698             // we have a scenario where updates need confirming.
46699             // eg. if a locking scenario exists..
46700             // we look for { errors : { needs_confirm : true }} in the response.
46701             if (
46702                 (typeof(action.result) != 'undefined')  &&
46703                 (typeof(action.result.errors) != 'undefined')  &&
46704                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46705            ){
46706                 var _t = this;
46707                 Roo.MessageBox.confirm(
46708                     "Change requires confirmation",
46709                     action.result.errorMsg,
46710                     function(r) {
46711                         if (r != 'yes') {
46712                             return;
46713                         }
46714                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46715                     }
46716                     
46717                 );
46718                 
46719                 
46720                 
46721                 return;
46722             }
46723             
46724             Roo.callback(o.failure, o.scope, [this, action]);
46725             // show an error message if no failed handler is set..
46726             if (!this.hasListener('actionfailed')) {
46727                 Roo.MessageBox.alert("Error",
46728                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46729                         action.result.errorMsg :
46730                         "Saving Failed, please check your entries or try again"
46731                 );
46732             }
46733             
46734             this.fireEvent('actionfailed', this, action);
46735         }
46736         
46737     },
46738
46739     /**
46740      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46741      * @param {String} id The value to search for
46742      * @return Field
46743      */
46744     findField : function(id){
46745         var field = this.items.get(id);
46746         if(!field){
46747             this.items.each(function(f){
46748                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46749                     field = f;
46750                     return false;
46751                 }
46752             });
46753         }
46754         return field || null;
46755     },
46756
46757     /**
46758      * Add a secondary form to this one, 
46759      * Used to provide tabbed forms. One form is primary, with hidden values 
46760      * which mirror the elements from the other forms.
46761      * 
46762      * @param {Roo.form.Form} form to add.
46763      * 
46764      */
46765     addForm : function(form)
46766     {
46767        
46768         if (this.childForms.indexOf(form) > -1) {
46769             // already added..
46770             return;
46771         }
46772         this.childForms.push(form);
46773         var n = '';
46774         Roo.each(form.allItems, function (fe) {
46775             
46776             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46777             if (this.findField(n)) { // already added..
46778                 return;
46779             }
46780             var add = new Roo.form.Hidden({
46781                 name : n
46782             });
46783             add.render(this.el);
46784             
46785             this.add( add );
46786         }, this);
46787         
46788     },
46789     /**
46790      * Mark fields in this form invalid in bulk.
46791      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46792      * @return {BasicForm} this
46793      */
46794     markInvalid : function(errors){
46795         if(errors instanceof Array){
46796             for(var i = 0, len = errors.length; i < len; i++){
46797                 var fieldError = errors[i];
46798                 var f = this.findField(fieldError.id);
46799                 if(f){
46800                     f.markInvalid(fieldError.msg);
46801                 }
46802             }
46803         }else{
46804             var field, id;
46805             for(id in errors){
46806                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46807                     field.markInvalid(errors[id]);
46808                 }
46809             }
46810         }
46811         Roo.each(this.childForms || [], function (f) {
46812             f.markInvalid(errors);
46813         });
46814         
46815         return this;
46816     },
46817
46818     /**
46819      * Set values for fields in this form in bulk.
46820      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46821      * @return {BasicForm} this
46822      */
46823     setValues : function(values){
46824         if(values instanceof Array){ // array of objects
46825             for(var i = 0, len = values.length; i < len; i++){
46826                 var v = values[i];
46827                 var f = this.findField(v.id);
46828                 if(f){
46829                     f.setValue(v.value);
46830                     if(this.trackResetOnLoad){
46831                         f.originalValue = f.getValue();
46832                     }
46833                 }
46834             }
46835         }else{ // object hash
46836             var field, id;
46837             for(id in values){
46838                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46839                     
46840                     if (field.setFromData && 
46841                         field.valueField && 
46842                         field.displayField &&
46843                         // combos' with local stores can 
46844                         // be queried via setValue()
46845                         // to set their value..
46846                         (field.store && !field.store.isLocal)
46847                         ) {
46848                         // it's a combo
46849                         var sd = { };
46850                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46851                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46852                         field.setFromData(sd);
46853                         
46854                     } else {
46855                         field.setValue(values[id]);
46856                     }
46857                     
46858                     
46859                     if(this.trackResetOnLoad){
46860                         field.originalValue = field.getValue();
46861                     }
46862                 }
46863             }
46864         }
46865         this.resetHasChanged();
46866         
46867         
46868         Roo.each(this.childForms || [], function (f) {
46869             f.setValues(values);
46870             f.resetHasChanged();
46871         });
46872                 
46873         return this;
46874     },
46875
46876     /**
46877      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
46878      * they are returned as an array.
46879      * @param {Boolean} asString
46880      * @return {Object}
46881      */
46882     getValues : function(asString){
46883         if (this.childForms) {
46884             // copy values from the child forms
46885             Roo.each(this.childForms, function (f) {
46886                 this.setValues(f.getValues());
46887             }, this);
46888         }
46889         
46890         
46891         
46892         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
46893         if(asString === true){
46894             return fs;
46895         }
46896         return Roo.urlDecode(fs);
46897     },
46898     
46899     /**
46900      * Returns the fields in this form as an object with key/value pairs. 
46901      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
46902      * @return {Object}
46903      */
46904     getFieldValues : function(with_hidden)
46905     {
46906         if (this.childForms) {
46907             // copy values from the child forms
46908             // should this call getFieldValues - probably not as we do not currently copy
46909             // hidden fields when we generate..
46910             Roo.each(this.childForms, function (f) {
46911                 this.setValues(f.getValues());
46912             }, this);
46913         }
46914         
46915         var ret = {};
46916         this.items.each(function(f){
46917             if (!f.getName()) {
46918                 return;
46919             }
46920             var v = f.getValue();
46921             if (f.inputType =='radio') {
46922                 if (typeof(ret[f.getName()]) == 'undefined') {
46923                     ret[f.getName()] = ''; // empty..
46924                 }
46925                 
46926                 if (!f.el.dom.checked) {
46927                     return;
46928                     
46929                 }
46930                 v = f.el.dom.value;
46931                 
46932             }
46933             
46934             // not sure if this supported any more..
46935             if ((typeof(v) == 'object') && f.getRawValue) {
46936                 v = f.getRawValue() ; // dates..
46937             }
46938             // combo boxes where name != hiddenName...
46939             if (f.name != f.getName()) {
46940                 ret[f.name] = f.getRawValue();
46941             }
46942             ret[f.getName()] = v;
46943         });
46944         
46945         return ret;
46946     },
46947
46948     /**
46949      * Clears all invalid messages in this form.
46950      * @return {BasicForm} this
46951      */
46952     clearInvalid : function(){
46953         this.items.each(function(f){
46954            f.clearInvalid();
46955         });
46956         
46957         Roo.each(this.childForms || [], function (f) {
46958             f.clearInvalid();
46959         });
46960         
46961         
46962         return this;
46963     },
46964
46965     /**
46966      * Resets this form.
46967      * @return {BasicForm} this
46968      */
46969     reset : function(){
46970         this.items.each(function(f){
46971             f.reset();
46972         });
46973         
46974         Roo.each(this.childForms || [], function (f) {
46975             f.reset();
46976         });
46977         this.resetHasChanged();
46978         
46979         return this;
46980     },
46981
46982     /**
46983      * Add Roo.form components to this form.
46984      * @param {Field} field1
46985      * @param {Field} field2 (optional)
46986      * @param {Field} etc (optional)
46987      * @return {BasicForm} this
46988      */
46989     add : function(){
46990         this.items.addAll(Array.prototype.slice.call(arguments, 0));
46991         return this;
46992     },
46993
46994
46995     /**
46996      * Removes a field from the items collection (does NOT remove its markup).
46997      * @param {Field} field
46998      * @return {BasicForm} this
46999      */
47000     remove : function(field){
47001         this.items.remove(field);
47002         return this;
47003     },
47004
47005     /**
47006      * Looks at the fields in this form, checks them for an id attribute,
47007      * and calls applyTo on the existing dom element with that id.
47008      * @return {BasicForm} this
47009      */
47010     render : function(){
47011         this.items.each(function(f){
47012             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47013                 f.applyTo(f.id);
47014             }
47015         });
47016         return this;
47017     },
47018
47019     /**
47020      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47021      * @param {Object} values
47022      * @return {BasicForm} this
47023      */
47024     applyToFields : function(o){
47025         this.items.each(function(f){
47026            Roo.apply(f, o);
47027         });
47028         return this;
47029     },
47030
47031     /**
47032      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47033      * @param {Object} values
47034      * @return {BasicForm} this
47035      */
47036     applyIfToFields : function(o){
47037         this.items.each(function(f){
47038            Roo.applyIf(f, o);
47039         });
47040         return this;
47041     }
47042 });
47043
47044 // back compat
47045 Roo.BasicForm = Roo.form.BasicForm;/*
47046  * Based on:
47047  * Ext JS Library 1.1.1
47048  * Copyright(c) 2006-2007, Ext JS, LLC.
47049  *
47050  * Originally Released Under LGPL - original licence link has changed is not relivant.
47051  *
47052  * Fork - LGPL
47053  * <script type="text/javascript">
47054  */
47055
47056 /**
47057  * @class Roo.form.Form
47058  * @extends Roo.form.BasicForm
47059  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47060  * @constructor
47061  * @param {Object} config Configuration options
47062  */
47063 Roo.form.Form = function(config){
47064     var xitems =  [];
47065     if (config.items) {
47066         xitems = config.items;
47067         delete config.items;
47068     }
47069    
47070     
47071     Roo.form.Form.superclass.constructor.call(this, null, config);
47072     this.url = this.url || this.action;
47073     if(!this.root){
47074         this.root = new Roo.form.Layout(Roo.applyIf({
47075             id: Roo.id()
47076         }, config));
47077     }
47078     this.active = this.root;
47079     /**
47080      * Array of all the buttons that have been added to this form via {@link addButton}
47081      * @type Array
47082      */
47083     this.buttons = [];
47084     this.allItems = [];
47085     this.addEvents({
47086         /**
47087          * @event clientvalidation
47088          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47089          * @param {Form} this
47090          * @param {Boolean} valid true if the form has passed client-side validation
47091          */
47092         clientvalidation: true,
47093         /**
47094          * @event rendered
47095          * Fires when the form is rendered
47096          * @param {Roo.form.Form} form
47097          */
47098         rendered : true
47099     });
47100     
47101     if (this.progressUrl) {
47102             // push a hidden field onto the list of fields..
47103             this.addxtype( {
47104                     xns: Roo.form, 
47105                     xtype : 'Hidden', 
47106                     name : 'UPLOAD_IDENTIFIER' 
47107             });
47108         }
47109         
47110     
47111     Roo.each(xitems, this.addxtype, this);
47112     
47113     
47114     
47115 };
47116
47117 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47118     /**
47119      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47120      */
47121     /**
47122      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47123      */
47124     /**
47125      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47126      */
47127     buttonAlign:'center',
47128
47129     /**
47130      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47131      */
47132     minButtonWidth:75,
47133
47134     /**
47135      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47136      * This property cascades to child containers if not set.
47137      */
47138     labelAlign:'left',
47139
47140     /**
47141      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47142      * fires a looping event with that state. This is required to bind buttons to the valid
47143      * state using the config value formBind:true on the button.
47144      */
47145     monitorValid : false,
47146
47147     /**
47148      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47149      */
47150     monitorPoll : 200,
47151     
47152     /**
47153      * @cfg {String} progressUrl - Url to return progress data 
47154      */
47155     
47156     progressUrl : false,
47157   
47158     /**
47159      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47160      * fields are added and the column is closed. If no fields are passed the column remains open
47161      * until end() is called.
47162      * @param {Object} config The config to pass to the column
47163      * @param {Field} field1 (optional)
47164      * @param {Field} field2 (optional)
47165      * @param {Field} etc (optional)
47166      * @return Column The column container object
47167      */
47168     column : function(c){
47169         var col = new Roo.form.Column(c);
47170         this.start(col);
47171         if(arguments.length > 1){ // duplicate code required because of Opera
47172             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47173             this.end();
47174         }
47175         return col;
47176     },
47177
47178     /**
47179      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47180      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47181      * until end() is called.
47182      * @param {Object} config The config to pass to the fieldset
47183      * @param {Field} field1 (optional)
47184      * @param {Field} field2 (optional)
47185      * @param {Field} etc (optional)
47186      * @return FieldSet The fieldset container object
47187      */
47188     fieldset : function(c){
47189         var fs = new Roo.form.FieldSet(c);
47190         this.start(fs);
47191         if(arguments.length > 1){ // duplicate code required because of Opera
47192             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47193             this.end();
47194         }
47195         return fs;
47196     },
47197
47198     /**
47199      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47200      * fields are added and the container is closed. If no fields are passed the container remains open
47201      * until end() is called.
47202      * @param {Object} config The config to pass to the Layout
47203      * @param {Field} field1 (optional)
47204      * @param {Field} field2 (optional)
47205      * @param {Field} etc (optional)
47206      * @return Layout The container object
47207      */
47208     container : function(c){
47209         var l = new Roo.form.Layout(c);
47210         this.start(l);
47211         if(arguments.length > 1){ // duplicate code required because of Opera
47212             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47213             this.end();
47214         }
47215         return l;
47216     },
47217
47218     /**
47219      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47220      * @param {Object} container A Roo.form.Layout or subclass of Layout
47221      * @return {Form} this
47222      */
47223     start : function(c){
47224         // cascade label info
47225         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47226         this.active.stack.push(c);
47227         c.ownerCt = this.active;
47228         this.active = c;
47229         return this;
47230     },
47231
47232     /**
47233      * Closes the current open container
47234      * @return {Form} this
47235      */
47236     end : function(){
47237         if(this.active == this.root){
47238             return this;
47239         }
47240         this.active = this.active.ownerCt;
47241         return this;
47242     },
47243
47244     /**
47245      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47246      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47247      * as the label of the field.
47248      * @param {Field} field1
47249      * @param {Field} field2 (optional)
47250      * @param {Field} etc. (optional)
47251      * @return {Form} this
47252      */
47253     add : function(){
47254         this.active.stack.push.apply(this.active.stack, arguments);
47255         this.allItems.push.apply(this.allItems,arguments);
47256         var r = [];
47257         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47258             if(a[i].isFormField){
47259                 r.push(a[i]);
47260             }
47261         }
47262         if(r.length > 0){
47263             Roo.form.Form.superclass.add.apply(this, r);
47264         }
47265         return this;
47266     },
47267     
47268
47269     
47270     
47271     
47272      /**
47273      * Find any element that has been added to a form, using it's ID or name
47274      * This can include framesets, columns etc. along with regular fields..
47275      * @param {String} id - id or name to find.
47276      
47277      * @return {Element} e - or false if nothing found.
47278      */
47279     findbyId : function(id)
47280     {
47281         var ret = false;
47282         if (!id) {
47283             return ret;
47284         }
47285         Roo.each(this.allItems, function(f){
47286             if (f.id == id || f.name == id ){
47287                 ret = f;
47288                 return false;
47289             }
47290         });
47291         return ret;
47292     },
47293
47294     
47295     
47296     /**
47297      * Render this form into the passed container. This should only be called once!
47298      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47299      * @return {Form} this
47300      */
47301     render : function(ct)
47302     {
47303         
47304         
47305         
47306         ct = Roo.get(ct);
47307         var o = this.autoCreate || {
47308             tag: 'form',
47309             method : this.method || 'POST',
47310             id : this.id || Roo.id()
47311         };
47312         this.initEl(ct.createChild(o));
47313
47314         this.root.render(this.el);
47315         
47316        
47317              
47318         this.items.each(function(f){
47319             f.render('x-form-el-'+f.id);
47320         });
47321
47322         if(this.buttons.length > 0){
47323             // tables are required to maintain order and for correct IE layout
47324             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47325                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47326                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47327             }}, null, true);
47328             var tr = tb.getElementsByTagName('tr')[0];
47329             for(var i = 0, len = this.buttons.length; i < len; i++) {
47330                 var b = this.buttons[i];
47331                 var td = document.createElement('td');
47332                 td.className = 'x-form-btn-td';
47333                 b.render(tr.appendChild(td));
47334             }
47335         }
47336         if(this.monitorValid){ // initialize after render
47337             this.startMonitoring();
47338         }
47339         this.fireEvent('rendered', this);
47340         return this;
47341     },
47342
47343     /**
47344      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47345      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47346      * object or a valid Roo.DomHelper element config
47347      * @param {Function} handler The function called when the button is clicked
47348      * @param {Object} scope (optional) The scope of the handler function
47349      * @return {Roo.Button}
47350      */
47351     addButton : function(config, handler, scope){
47352         var bc = {
47353             handler: handler,
47354             scope: scope,
47355             minWidth: this.minButtonWidth,
47356             hideParent:true
47357         };
47358         if(typeof config == "string"){
47359             bc.text = config;
47360         }else{
47361             Roo.apply(bc, config);
47362         }
47363         var btn = new Roo.Button(null, bc);
47364         this.buttons.push(btn);
47365         return btn;
47366     },
47367
47368      /**
47369      * Adds a series of form elements (using the xtype property as the factory method.
47370      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47371      * @param {Object} config 
47372      */
47373     
47374     addxtype : function()
47375     {
47376         var ar = Array.prototype.slice.call(arguments, 0);
47377         var ret = false;
47378         for(var i = 0; i < ar.length; i++) {
47379             if (!ar[i]) {
47380                 continue; // skip -- if this happends something invalid got sent, we 
47381                 // should ignore it, as basically that interface element will not show up
47382                 // and that should be pretty obvious!!
47383             }
47384             
47385             if (Roo.form[ar[i].xtype]) {
47386                 ar[i].form = this;
47387                 var fe = Roo.factory(ar[i], Roo.form);
47388                 if (!ret) {
47389                     ret = fe;
47390                 }
47391                 fe.form = this;
47392                 if (fe.store) {
47393                     fe.store.form = this;
47394                 }
47395                 if (fe.isLayout) {  
47396                          
47397                     this.start(fe);
47398                     this.allItems.push(fe);
47399                     if (fe.items && fe.addxtype) {
47400                         fe.addxtype.apply(fe, fe.items);
47401                         delete fe.items;
47402                     }
47403                      this.end();
47404                     continue;
47405                 }
47406                 
47407                 
47408                  
47409                 this.add(fe);
47410               //  console.log('adding ' + ar[i].xtype);
47411             }
47412             if (ar[i].xtype == 'Button') {  
47413                 //console.log('adding button');
47414                 //console.log(ar[i]);
47415                 this.addButton(ar[i]);
47416                 this.allItems.push(fe);
47417                 continue;
47418             }
47419             
47420             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47421                 alert('end is not supported on xtype any more, use items');
47422             //    this.end();
47423             //    //console.log('adding end');
47424             }
47425             
47426         }
47427         return ret;
47428     },
47429     
47430     /**
47431      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47432      * option "monitorValid"
47433      */
47434     startMonitoring : function(){
47435         if(!this.bound){
47436             this.bound = true;
47437             Roo.TaskMgr.start({
47438                 run : this.bindHandler,
47439                 interval : this.monitorPoll || 200,
47440                 scope: this
47441             });
47442         }
47443     },
47444
47445     /**
47446      * Stops monitoring of the valid state of this form
47447      */
47448     stopMonitoring : function(){
47449         this.bound = false;
47450     },
47451
47452     // private
47453     bindHandler : function(){
47454         if(!this.bound){
47455             return false; // stops binding
47456         }
47457         var valid = true;
47458         this.items.each(function(f){
47459             if(!f.isValid(true)){
47460                 valid = false;
47461                 return false;
47462             }
47463         });
47464         for(var i = 0, len = this.buttons.length; i < len; i++){
47465             var btn = this.buttons[i];
47466             if(btn.formBind === true && btn.disabled === valid){
47467                 btn.setDisabled(!valid);
47468             }
47469         }
47470         this.fireEvent('clientvalidation', this, valid);
47471     }
47472     
47473     
47474     
47475     
47476     
47477     
47478     
47479     
47480 });
47481
47482
47483 // back compat
47484 Roo.Form = Roo.form.Form;
47485 /*
47486  * Based on:
47487  * Ext JS Library 1.1.1
47488  * Copyright(c) 2006-2007, Ext JS, LLC.
47489  *
47490  * Originally Released Under LGPL - original licence link has changed is not relivant.
47491  *
47492  * Fork - LGPL
47493  * <script type="text/javascript">
47494  */
47495
47496 // as we use this in bootstrap.
47497 Roo.namespace('Roo.form');
47498  /**
47499  * @class Roo.form.Action
47500  * Internal Class used to handle form actions
47501  * @constructor
47502  * @param {Roo.form.BasicForm} el The form element or its id
47503  * @param {Object} config Configuration options
47504  */
47505
47506  
47507  
47508 // define the action interface
47509 Roo.form.Action = function(form, options){
47510     this.form = form;
47511     this.options = options || {};
47512 };
47513 /**
47514  * Client Validation Failed
47515  * @const 
47516  */
47517 Roo.form.Action.CLIENT_INVALID = 'client';
47518 /**
47519  * Server Validation Failed
47520  * @const 
47521  */
47522 Roo.form.Action.SERVER_INVALID = 'server';
47523  /**
47524  * Connect to Server Failed
47525  * @const 
47526  */
47527 Roo.form.Action.CONNECT_FAILURE = 'connect';
47528 /**
47529  * Reading Data from Server Failed
47530  * @const 
47531  */
47532 Roo.form.Action.LOAD_FAILURE = 'load';
47533
47534 Roo.form.Action.prototype = {
47535     type : 'default',
47536     failureType : undefined,
47537     response : undefined,
47538     result : undefined,
47539
47540     // interface method
47541     run : function(options){
47542
47543     },
47544
47545     // interface method
47546     success : function(response){
47547
47548     },
47549
47550     // interface method
47551     handleResponse : function(response){
47552
47553     },
47554
47555     // default connection failure
47556     failure : function(response){
47557         
47558         this.response = response;
47559         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47560         this.form.afterAction(this, false);
47561     },
47562
47563     processResponse : function(response){
47564         this.response = response;
47565         if(!response.responseText){
47566             return true;
47567         }
47568         this.result = this.handleResponse(response);
47569         return this.result;
47570     },
47571
47572     // utility functions used internally
47573     getUrl : function(appendParams){
47574         var url = this.options.url || this.form.url || this.form.el.dom.action;
47575         if(appendParams){
47576             var p = this.getParams();
47577             if(p){
47578                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47579             }
47580         }
47581         return url;
47582     },
47583
47584     getMethod : function(){
47585         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47586     },
47587
47588     getParams : function(){
47589         var bp = this.form.baseParams;
47590         var p = this.options.params;
47591         if(p){
47592             if(typeof p == "object"){
47593                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47594             }else if(typeof p == 'string' && bp){
47595                 p += '&' + Roo.urlEncode(bp);
47596             }
47597         }else if(bp){
47598             p = Roo.urlEncode(bp);
47599         }
47600         return p;
47601     },
47602
47603     createCallback : function(){
47604         return {
47605             success: this.success,
47606             failure: this.failure,
47607             scope: this,
47608             timeout: (this.form.timeout*1000),
47609             upload: this.form.fileUpload ? this.success : undefined
47610         };
47611     }
47612 };
47613
47614 Roo.form.Action.Submit = function(form, options){
47615     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47616 };
47617
47618 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47619     type : 'submit',
47620
47621     haveProgress : false,
47622     uploadComplete : false,
47623     
47624     // uploadProgress indicator.
47625     uploadProgress : function()
47626     {
47627         if (!this.form.progressUrl) {
47628             return;
47629         }
47630         
47631         if (!this.haveProgress) {
47632             Roo.MessageBox.progress("Uploading", "Uploading");
47633         }
47634         if (this.uploadComplete) {
47635            Roo.MessageBox.hide();
47636            return;
47637         }
47638         
47639         this.haveProgress = true;
47640    
47641         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47642         
47643         var c = new Roo.data.Connection();
47644         c.request({
47645             url : this.form.progressUrl,
47646             params: {
47647                 id : uid
47648             },
47649             method: 'GET',
47650             success : function(req){
47651                //console.log(data);
47652                 var rdata = false;
47653                 var edata;
47654                 try  {
47655                    rdata = Roo.decode(req.responseText)
47656                 } catch (e) {
47657                     Roo.log("Invalid data from server..");
47658                     Roo.log(edata);
47659                     return;
47660                 }
47661                 if (!rdata || !rdata.success) {
47662                     Roo.log(rdata);
47663                     Roo.MessageBox.alert(Roo.encode(rdata));
47664                     return;
47665                 }
47666                 var data = rdata.data;
47667                 
47668                 if (this.uploadComplete) {
47669                    Roo.MessageBox.hide();
47670                    return;
47671                 }
47672                    
47673                 if (data){
47674                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47675                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47676                     );
47677                 }
47678                 this.uploadProgress.defer(2000,this);
47679             },
47680        
47681             failure: function(data) {
47682                 Roo.log('progress url failed ');
47683                 Roo.log(data);
47684             },
47685             scope : this
47686         });
47687            
47688     },
47689     
47690     
47691     run : function()
47692     {
47693         // run get Values on the form, so it syncs any secondary forms.
47694         this.form.getValues();
47695         
47696         var o = this.options;
47697         var method = this.getMethod();
47698         var isPost = method == 'POST';
47699         if(o.clientValidation === false || this.form.isValid()){
47700             
47701             if (this.form.progressUrl) {
47702                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47703                     (new Date() * 1) + '' + Math.random());
47704                     
47705             } 
47706             
47707             
47708             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47709                 form:this.form.el.dom,
47710                 url:this.getUrl(!isPost),
47711                 method: method,
47712                 params:isPost ? this.getParams() : null,
47713                 isUpload: this.form.fileUpload
47714             }));
47715             
47716             this.uploadProgress();
47717
47718         }else if (o.clientValidation !== false){ // client validation failed
47719             this.failureType = Roo.form.Action.CLIENT_INVALID;
47720             this.form.afterAction(this, false);
47721         }
47722     },
47723
47724     success : function(response)
47725     {
47726         this.uploadComplete= true;
47727         if (this.haveProgress) {
47728             Roo.MessageBox.hide();
47729         }
47730         
47731         
47732         var result = this.processResponse(response);
47733         if(result === true || result.success){
47734             this.form.afterAction(this, true);
47735             return;
47736         }
47737         if(result.errors){
47738             this.form.markInvalid(result.errors);
47739             this.failureType = Roo.form.Action.SERVER_INVALID;
47740         }
47741         this.form.afterAction(this, false);
47742     },
47743     failure : function(response)
47744     {
47745         this.uploadComplete= true;
47746         if (this.haveProgress) {
47747             Roo.MessageBox.hide();
47748         }
47749         
47750         this.response = response;
47751         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47752         this.form.afterAction(this, false);
47753     },
47754     
47755     handleResponse : function(response){
47756         if(this.form.errorReader){
47757             var rs = this.form.errorReader.read(response);
47758             var errors = [];
47759             if(rs.records){
47760                 for(var i = 0, len = rs.records.length; i < len; i++) {
47761                     var r = rs.records[i];
47762                     errors[i] = r.data;
47763                 }
47764             }
47765             if(errors.length < 1){
47766                 errors = null;
47767             }
47768             return {
47769                 success : rs.success,
47770                 errors : errors
47771             };
47772         }
47773         var ret = false;
47774         try {
47775             ret = Roo.decode(response.responseText);
47776         } catch (e) {
47777             ret = {
47778                 success: false,
47779                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47780                 errors : []
47781             };
47782         }
47783         return ret;
47784         
47785     }
47786 });
47787
47788
47789 Roo.form.Action.Load = function(form, options){
47790     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47791     this.reader = this.form.reader;
47792 };
47793
47794 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47795     type : 'load',
47796
47797     run : function(){
47798         
47799         Roo.Ajax.request(Roo.apply(
47800                 this.createCallback(), {
47801                     method:this.getMethod(),
47802                     url:this.getUrl(false),
47803                     params:this.getParams()
47804         }));
47805     },
47806
47807     success : function(response){
47808         
47809         var result = this.processResponse(response);
47810         if(result === true || !result.success || !result.data){
47811             this.failureType = Roo.form.Action.LOAD_FAILURE;
47812             this.form.afterAction(this, false);
47813             return;
47814         }
47815         this.form.clearInvalid();
47816         this.form.setValues(result.data);
47817         this.form.afterAction(this, true);
47818     },
47819
47820     handleResponse : function(response){
47821         if(this.form.reader){
47822             var rs = this.form.reader.read(response);
47823             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47824             return {
47825                 success : rs.success,
47826                 data : data
47827             };
47828         }
47829         return Roo.decode(response.responseText);
47830     }
47831 });
47832
47833 Roo.form.Action.ACTION_TYPES = {
47834     'load' : Roo.form.Action.Load,
47835     'submit' : Roo.form.Action.Submit
47836 };/*
47837  * Based on:
47838  * Ext JS Library 1.1.1
47839  * Copyright(c) 2006-2007, Ext JS, LLC.
47840  *
47841  * Originally Released Under LGPL - original licence link has changed is not relivant.
47842  *
47843  * Fork - LGPL
47844  * <script type="text/javascript">
47845  */
47846  
47847 /**
47848  * @class Roo.form.Layout
47849  * @extends Roo.Component
47850  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47851  * @constructor
47852  * @param {Object} config Configuration options
47853  */
47854 Roo.form.Layout = function(config){
47855     var xitems = [];
47856     if (config.items) {
47857         xitems = config.items;
47858         delete config.items;
47859     }
47860     Roo.form.Layout.superclass.constructor.call(this, config);
47861     this.stack = [];
47862     Roo.each(xitems, this.addxtype, this);
47863      
47864 };
47865
47866 Roo.extend(Roo.form.Layout, Roo.Component, {
47867     /**
47868      * @cfg {String/Object} autoCreate
47869      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
47870      */
47871     /**
47872      * @cfg {String/Object/Function} style
47873      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
47874      * a function which returns such a specification.
47875      */
47876     /**
47877      * @cfg {String} labelAlign
47878      * Valid values are "left," "top" and "right" (defaults to "left")
47879      */
47880     /**
47881      * @cfg {Number} labelWidth
47882      * Fixed width in pixels of all field labels (defaults to undefined)
47883      */
47884     /**
47885      * @cfg {Boolean} clear
47886      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
47887      */
47888     clear : true,
47889     /**
47890      * @cfg {String} labelSeparator
47891      * The separator to use after field labels (defaults to ':')
47892      */
47893     labelSeparator : ':',
47894     /**
47895      * @cfg {Boolean} hideLabels
47896      * True to suppress the display of field labels in this layout (defaults to false)
47897      */
47898     hideLabels : false,
47899
47900     // private
47901     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
47902     
47903     isLayout : true,
47904     
47905     // private
47906     onRender : function(ct, position){
47907         if(this.el){ // from markup
47908             this.el = Roo.get(this.el);
47909         }else {  // generate
47910             var cfg = this.getAutoCreate();
47911             this.el = ct.createChild(cfg, position);
47912         }
47913         if(this.style){
47914             this.el.applyStyles(this.style);
47915         }
47916         if(this.labelAlign){
47917             this.el.addClass('x-form-label-'+this.labelAlign);
47918         }
47919         if(this.hideLabels){
47920             this.labelStyle = "display:none";
47921             this.elementStyle = "padding-left:0;";
47922         }else{
47923             if(typeof this.labelWidth == 'number'){
47924                 this.labelStyle = "width:"+this.labelWidth+"px;";
47925                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
47926             }
47927             if(this.labelAlign == 'top'){
47928                 this.labelStyle = "width:auto;";
47929                 this.elementStyle = "padding-left:0;";
47930             }
47931         }
47932         var stack = this.stack;
47933         var slen = stack.length;
47934         if(slen > 0){
47935             if(!this.fieldTpl){
47936                 var t = new Roo.Template(
47937                     '<div class="x-form-item {5}">',
47938                         '<label for="{0}" style="{2}">{1}{4}</label>',
47939                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
47940                         '</div>',
47941                     '</div><div class="x-form-clear-left"></div>'
47942                 );
47943                 t.disableFormats = true;
47944                 t.compile();
47945                 Roo.form.Layout.prototype.fieldTpl = t;
47946             }
47947             for(var i = 0; i < slen; i++) {
47948                 if(stack[i].isFormField){
47949                     this.renderField(stack[i]);
47950                 }else{
47951                     this.renderComponent(stack[i]);
47952                 }
47953             }
47954         }
47955         if(this.clear){
47956             this.el.createChild({cls:'x-form-clear'});
47957         }
47958     },
47959
47960     // private
47961     renderField : function(f){
47962         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
47963                f.id, //0
47964                f.fieldLabel, //1
47965                f.labelStyle||this.labelStyle||'', //2
47966                this.elementStyle||'', //3
47967                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
47968                f.itemCls||this.itemCls||''  //5
47969        ], true).getPrevSibling());
47970     },
47971
47972     // private
47973     renderComponent : function(c){
47974         c.render(c.isLayout ? this.el : this.el.createChild());    
47975     },
47976     /**
47977      * Adds a object form elements (using the xtype property as the factory method.)
47978      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
47979      * @param {Object} config 
47980      */
47981     addxtype : function(o)
47982     {
47983         // create the lement.
47984         o.form = this.form;
47985         var fe = Roo.factory(o, Roo.form);
47986         this.form.allItems.push(fe);
47987         this.stack.push(fe);
47988         
47989         if (fe.isFormField) {
47990             this.form.items.add(fe);
47991         }
47992          
47993         return fe;
47994     }
47995 });
47996
47997 /**
47998  * @class Roo.form.Column
47999  * @extends Roo.form.Layout
48000  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48001  * @constructor
48002  * @param {Object} config Configuration options
48003  */
48004 Roo.form.Column = function(config){
48005     Roo.form.Column.superclass.constructor.call(this, config);
48006 };
48007
48008 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48009     /**
48010      * @cfg {Number/String} width
48011      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48012      */
48013     /**
48014      * @cfg {String/Object} autoCreate
48015      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48016      */
48017
48018     // private
48019     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48020
48021     // private
48022     onRender : function(ct, position){
48023         Roo.form.Column.superclass.onRender.call(this, ct, position);
48024         if(this.width){
48025             this.el.setWidth(this.width);
48026         }
48027     }
48028 });
48029
48030
48031 /**
48032  * @class Roo.form.Row
48033  * @extends Roo.form.Layout
48034  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48035  * @constructor
48036  * @param {Object} config Configuration options
48037  */
48038
48039  
48040 Roo.form.Row = function(config){
48041     Roo.form.Row.superclass.constructor.call(this, config);
48042 };
48043  
48044 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48045       /**
48046      * @cfg {Number/String} width
48047      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48048      */
48049     /**
48050      * @cfg {Number/String} height
48051      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48052      */
48053     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48054     
48055     padWidth : 20,
48056     // private
48057     onRender : function(ct, position){
48058         //console.log('row render');
48059         if(!this.rowTpl){
48060             var t = new Roo.Template(
48061                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48062                     '<label for="{0}" style="{2}">{1}{4}</label>',
48063                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48064                     '</div>',
48065                 '</div>'
48066             );
48067             t.disableFormats = true;
48068             t.compile();
48069             Roo.form.Layout.prototype.rowTpl = t;
48070         }
48071         this.fieldTpl = this.rowTpl;
48072         
48073         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48074         var labelWidth = 100;
48075         
48076         if ((this.labelAlign != 'top')) {
48077             if (typeof this.labelWidth == 'number') {
48078                 labelWidth = this.labelWidth
48079             }
48080             this.padWidth =  20 + labelWidth;
48081             
48082         }
48083         
48084         Roo.form.Column.superclass.onRender.call(this, ct, position);
48085         if(this.width){
48086             this.el.setWidth(this.width);
48087         }
48088         if(this.height){
48089             this.el.setHeight(this.height);
48090         }
48091     },
48092     
48093     // private
48094     renderField : function(f){
48095         f.fieldEl = this.fieldTpl.append(this.el, [
48096                f.id, f.fieldLabel,
48097                f.labelStyle||this.labelStyle||'',
48098                this.elementStyle||'',
48099                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48100                f.itemCls||this.itemCls||'',
48101                f.width ? f.width + this.padWidth : 160 + this.padWidth
48102        ],true);
48103     }
48104 });
48105  
48106
48107 /**
48108  * @class Roo.form.FieldSet
48109  * @extends Roo.form.Layout
48110  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48111  * @constructor
48112  * @param {Object} config Configuration options
48113  */
48114 Roo.form.FieldSet = function(config){
48115     Roo.form.FieldSet.superclass.constructor.call(this, config);
48116 };
48117
48118 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48119     /**
48120      * @cfg {String} legend
48121      * The text to display as the legend for the FieldSet (defaults to '')
48122      */
48123     /**
48124      * @cfg {String/Object} autoCreate
48125      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48126      */
48127
48128     // private
48129     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48130
48131     // private
48132     onRender : function(ct, position){
48133         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48134         if(this.legend){
48135             this.setLegend(this.legend);
48136         }
48137     },
48138
48139     // private
48140     setLegend : function(text){
48141         if(this.rendered){
48142             this.el.child('legend').update(text);
48143         }
48144     }
48145 });/*
48146  * Based on:
48147  * Ext JS Library 1.1.1
48148  * Copyright(c) 2006-2007, Ext JS, LLC.
48149  *
48150  * Originally Released Under LGPL - original licence link has changed is not relivant.
48151  *
48152  * Fork - LGPL
48153  * <script type="text/javascript">
48154  */
48155 /**
48156  * @class Roo.form.VTypes
48157  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48158  * @singleton
48159  */
48160 Roo.form.VTypes = function(){
48161     // closure these in so they are only created once.
48162     var alpha = /^[a-zA-Z_]+$/;
48163     var alphanum = /^[a-zA-Z0-9_]+$/;
48164     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48165     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48166
48167     // All these messages and functions are configurable
48168     return {
48169         /**
48170          * The function used to validate email addresses
48171          * @param {String} value The email address
48172          */
48173         'email' : function(v){
48174             return email.test(v);
48175         },
48176         /**
48177          * The error text to display when the email validation function returns false
48178          * @type String
48179          */
48180         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48181         /**
48182          * The keystroke filter mask to be applied on email input
48183          * @type RegExp
48184          */
48185         'emailMask' : /[a-z0-9_\.\-@]/i,
48186
48187         /**
48188          * The function used to validate URLs
48189          * @param {String} value The URL
48190          */
48191         'url' : function(v){
48192             return url.test(v);
48193         },
48194         /**
48195          * The error text to display when the url validation function returns false
48196          * @type String
48197          */
48198         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48199         
48200         /**
48201          * The function used to validate alpha values
48202          * @param {String} value The value
48203          */
48204         'alpha' : function(v){
48205             return alpha.test(v);
48206         },
48207         /**
48208          * The error text to display when the alpha validation function returns false
48209          * @type String
48210          */
48211         'alphaText' : 'This field should only contain letters and _',
48212         /**
48213          * The keystroke filter mask to be applied on alpha input
48214          * @type RegExp
48215          */
48216         'alphaMask' : /[a-z_]/i,
48217
48218         /**
48219          * The function used to validate alphanumeric values
48220          * @param {String} value The value
48221          */
48222         'alphanum' : function(v){
48223             return alphanum.test(v);
48224         },
48225         /**
48226          * The error text to display when the alphanumeric validation function returns false
48227          * @type String
48228          */
48229         'alphanumText' : 'This field should only contain letters, numbers and _',
48230         /**
48231          * The keystroke filter mask to be applied on alphanumeric input
48232          * @type RegExp
48233          */
48234         'alphanumMask' : /[a-z0-9_]/i
48235     };
48236 }();//<script type="text/javascript">
48237
48238 /**
48239  * @class Roo.form.FCKeditor
48240  * @extends Roo.form.TextArea
48241  * Wrapper around the FCKEditor http://www.fckeditor.net
48242  * @constructor
48243  * Creates a new FCKeditor
48244  * @param {Object} config Configuration options
48245  */
48246 Roo.form.FCKeditor = function(config){
48247     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48248     this.addEvents({
48249          /**
48250          * @event editorinit
48251          * Fired when the editor is initialized - you can add extra handlers here..
48252          * @param {FCKeditor} this
48253          * @param {Object} the FCK object.
48254          */
48255         editorinit : true
48256     });
48257     
48258     
48259 };
48260 Roo.form.FCKeditor.editors = { };
48261 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48262 {
48263     //defaultAutoCreate : {
48264     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48265     //},
48266     // private
48267     /**
48268      * @cfg {Object} fck options - see fck manual for details.
48269      */
48270     fckconfig : false,
48271     
48272     /**
48273      * @cfg {Object} fck toolbar set (Basic or Default)
48274      */
48275     toolbarSet : 'Basic',
48276     /**
48277      * @cfg {Object} fck BasePath
48278      */ 
48279     basePath : '/fckeditor/',
48280     
48281     
48282     frame : false,
48283     
48284     value : '',
48285     
48286    
48287     onRender : function(ct, position)
48288     {
48289         if(!this.el){
48290             this.defaultAutoCreate = {
48291                 tag: "textarea",
48292                 style:"width:300px;height:60px;",
48293                 autocomplete: "new-password"
48294             };
48295         }
48296         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48297         /*
48298         if(this.grow){
48299             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48300             if(this.preventScrollbars){
48301                 this.el.setStyle("overflow", "hidden");
48302             }
48303             this.el.setHeight(this.growMin);
48304         }
48305         */
48306         //console.log('onrender' + this.getId() );
48307         Roo.form.FCKeditor.editors[this.getId()] = this;
48308          
48309
48310         this.replaceTextarea() ;
48311         
48312     },
48313     
48314     getEditor : function() {
48315         return this.fckEditor;
48316     },
48317     /**
48318      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48319      * @param {Mixed} value The value to set
48320      */
48321     
48322     
48323     setValue : function(value)
48324     {
48325         //console.log('setValue: ' + value);
48326         
48327         if(typeof(value) == 'undefined') { // not sure why this is happending...
48328             return;
48329         }
48330         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48331         
48332         //if(!this.el || !this.getEditor()) {
48333         //    this.value = value;
48334             //this.setValue.defer(100,this,[value]);    
48335         //    return;
48336         //} 
48337         
48338         if(!this.getEditor()) {
48339             return;
48340         }
48341         
48342         this.getEditor().SetData(value);
48343         
48344         //
48345
48346     },
48347
48348     /**
48349      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48350      * @return {Mixed} value The field value
48351      */
48352     getValue : function()
48353     {
48354         
48355         if (this.frame && this.frame.dom.style.display == 'none') {
48356             return Roo.form.FCKeditor.superclass.getValue.call(this);
48357         }
48358         
48359         if(!this.el || !this.getEditor()) {
48360            
48361            // this.getValue.defer(100,this); 
48362             return this.value;
48363         }
48364        
48365         
48366         var value=this.getEditor().GetData();
48367         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48368         return Roo.form.FCKeditor.superclass.getValue.call(this);
48369         
48370
48371     },
48372
48373     /**
48374      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48375      * @return {Mixed} value The field value
48376      */
48377     getRawValue : function()
48378     {
48379         if (this.frame && this.frame.dom.style.display == 'none') {
48380             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48381         }
48382         
48383         if(!this.el || !this.getEditor()) {
48384             //this.getRawValue.defer(100,this); 
48385             return this.value;
48386             return;
48387         }
48388         
48389         
48390         
48391         var value=this.getEditor().GetData();
48392         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48393         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48394          
48395     },
48396     
48397     setSize : function(w,h) {
48398         
48399         
48400         
48401         //if (this.frame && this.frame.dom.style.display == 'none') {
48402         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48403         //    return;
48404         //}
48405         //if(!this.el || !this.getEditor()) {
48406         //    this.setSize.defer(100,this, [w,h]); 
48407         //    return;
48408         //}
48409         
48410         
48411         
48412         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48413         
48414         this.frame.dom.setAttribute('width', w);
48415         this.frame.dom.setAttribute('height', h);
48416         this.frame.setSize(w,h);
48417         
48418     },
48419     
48420     toggleSourceEdit : function(value) {
48421         
48422       
48423          
48424         this.el.dom.style.display = value ? '' : 'none';
48425         this.frame.dom.style.display = value ?  'none' : '';
48426         
48427     },
48428     
48429     
48430     focus: function(tag)
48431     {
48432         if (this.frame.dom.style.display == 'none') {
48433             return Roo.form.FCKeditor.superclass.focus.call(this);
48434         }
48435         if(!this.el || !this.getEditor()) {
48436             this.focus.defer(100,this, [tag]); 
48437             return;
48438         }
48439         
48440         
48441         
48442         
48443         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48444         this.getEditor().Focus();
48445         if (tgs.length) {
48446             if (!this.getEditor().Selection.GetSelection()) {
48447                 this.focus.defer(100,this, [tag]); 
48448                 return;
48449             }
48450             
48451             
48452             var r = this.getEditor().EditorDocument.createRange();
48453             r.setStart(tgs[0],0);
48454             r.setEnd(tgs[0],0);
48455             this.getEditor().Selection.GetSelection().removeAllRanges();
48456             this.getEditor().Selection.GetSelection().addRange(r);
48457             this.getEditor().Focus();
48458         }
48459         
48460     },
48461     
48462     
48463     
48464     replaceTextarea : function()
48465     {
48466         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48467             return ;
48468         }
48469         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48470         //{
48471             // We must check the elements firstly using the Id and then the name.
48472         var oTextarea = document.getElementById( this.getId() );
48473         
48474         var colElementsByName = document.getElementsByName( this.getId() ) ;
48475          
48476         oTextarea.style.display = 'none' ;
48477
48478         if ( oTextarea.tabIndex ) {            
48479             this.TabIndex = oTextarea.tabIndex ;
48480         }
48481         
48482         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48483         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48484         this.frame = Roo.get(this.getId() + '___Frame')
48485     },
48486     
48487     _getConfigHtml : function()
48488     {
48489         var sConfig = '' ;
48490
48491         for ( var o in this.fckconfig ) {
48492             sConfig += sConfig.length > 0  ? '&amp;' : '';
48493             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48494         }
48495
48496         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48497     },
48498     
48499     
48500     _getIFrameHtml : function()
48501     {
48502         var sFile = 'fckeditor.html' ;
48503         /* no idea what this is about..
48504         try
48505         {
48506             if ( (/fcksource=true/i).test( window.top.location.search ) )
48507                 sFile = 'fckeditor.original.html' ;
48508         }
48509         catch (e) { 
48510         */
48511
48512         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48513         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48514         
48515         
48516         var html = '<iframe id="' + this.getId() +
48517             '___Frame" src="' + sLink +
48518             '" width="' + this.width +
48519             '" height="' + this.height + '"' +
48520             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48521             ' frameborder="0" scrolling="no"></iframe>' ;
48522
48523         return html ;
48524     },
48525     
48526     _insertHtmlBefore : function( html, element )
48527     {
48528         if ( element.insertAdjacentHTML )       {
48529             // IE
48530             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48531         } else { // Gecko
48532             var oRange = document.createRange() ;
48533             oRange.setStartBefore( element ) ;
48534             var oFragment = oRange.createContextualFragment( html );
48535             element.parentNode.insertBefore( oFragment, element ) ;
48536         }
48537     }
48538     
48539     
48540   
48541     
48542     
48543     
48544     
48545
48546 });
48547
48548 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48549
48550 function FCKeditor_OnComplete(editorInstance){
48551     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48552     f.fckEditor = editorInstance;
48553     //console.log("loaded");
48554     f.fireEvent('editorinit', f, editorInstance);
48555
48556   
48557
48558  
48559
48560
48561
48562
48563
48564
48565
48566
48567
48568
48569
48570
48571
48572
48573
48574 //<script type="text/javascript">
48575 /**
48576  * @class Roo.form.GridField
48577  * @extends Roo.form.Field
48578  * Embed a grid (or editable grid into a form)
48579  * STATUS ALPHA
48580  * 
48581  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48582  * it needs 
48583  * xgrid.store = Roo.data.Store
48584  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48585  * xgrid.store.reader = Roo.data.JsonReader 
48586  * 
48587  * 
48588  * @constructor
48589  * Creates a new GridField
48590  * @param {Object} config Configuration options
48591  */
48592 Roo.form.GridField = function(config){
48593     Roo.form.GridField.superclass.constructor.call(this, config);
48594      
48595 };
48596
48597 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48598     /**
48599      * @cfg {Number} width  - used to restrict width of grid..
48600      */
48601     width : 100,
48602     /**
48603      * @cfg {Number} height - used to restrict height of grid..
48604      */
48605     height : 50,
48606      /**
48607      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48608          * 
48609          *}
48610      */
48611     xgrid : false, 
48612     /**
48613      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48614      * {tag: "input", type: "checkbox", autocomplete: "off"})
48615      */
48616    // defaultAutoCreate : { tag: 'div' },
48617     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48618     /**
48619      * @cfg {String} addTitle Text to include for adding a title.
48620      */
48621     addTitle : false,
48622     //
48623     onResize : function(){
48624         Roo.form.Field.superclass.onResize.apply(this, arguments);
48625     },
48626
48627     initEvents : function(){
48628         // Roo.form.Checkbox.superclass.initEvents.call(this);
48629         // has no events...
48630        
48631     },
48632
48633
48634     getResizeEl : function(){
48635         return this.wrap;
48636     },
48637
48638     getPositionEl : function(){
48639         return this.wrap;
48640     },
48641
48642     // private
48643     onRender : function(ct, position){
48644         
48645         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48646         var style = this.style;
48647         delete this.style;
48648         
48649         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48650         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48651         this.viewEl = this.wrap.createChild({ tag: 'div' });
48652         if (style) {
48653             this.viewEl.applyStyles(style);
48654         }
48655         if (this.width) {
48656             this.viewEl.setWidth(this.width);
48657         }
48658         if (this.height) {
48659             this.viewEl.setHeight(this.height);
48660         }
48661         //if(this.inputValue !== undefined){
48662         //this.setValue(this.value);
48663         
48664         
48665         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48666         
48667         
48668         this.grid.render();
48669         this.grid.getDataSource().on('remove', this.refreshValue, this);
48670         this.grid.getDataSource().on('update', this.refreshValue, this);
48671         this.grid.on('afteredit', this.refreshValue, this);
48672  
48673     },
48674      
48675     
48676     /**
48677      * Sets the value of the item. 
48678      * @param {String} either an object  or a string..
48679      */
48680     setValue : function(v){
48681         //this.value = v;
48682         v = v || []; // empty set..
48683         // this does not seem smart - it really only affects memoryproxy grids..
48684         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48685             var ds = this.grid.getDataSource();
48686             // assumes a json reader..
48687             var data = {}
48688             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48689             ds.loadData( data);
48690         }
48691         // clear selection so it does not get stale.
48692         if (this.grid.sm) { 
48693             this.grid.sm.clearSelections();
48694         }
48695         
48696         Roo.form.GridField.superclass.setValue.call(this, v);
48697         this.refreshValue();
48698         // should load data in the grid really....
48699     },
48700     
48701     // private
48702     refreshValue: function() {
48703          var val = [];
48704         this.grid.getDataSource().each(function(r) {
48705             val.push(r.data);
48706         });
48707         this.el.dom.value = Roo.encode(val);
48708     }
48709     
48710      
48711     
48712     
48713 });/*
48714  * Based on:
48715  * Ext JS Library 1.1.1
48716  * Copyright(c) 2006-2007, Ext JS, LLC.
48717  *
48718  * Originally Released Under LGPL - original licence link has changed is not relivant.
48719  *
48720  * Fork - LGPL
48721  * <script type="text/javascript">
48722  */
48723 /**
48724  * @class Roo.form.DisplayField
48725  * @extends Roo.form.Field
48726  * A generic Field to display non-editable data.
48727  * @cfg {Boolean} closable (true|false) default false
48728  * @constructor
48729  * Creates a new Display Field item.
48730  * @param {Object} config Configuration options
48731  */
48732 Roo.form.DisplayField = function(config){
48733     Roo.form.DisplayField.superclass.constructor.call(this, config);
48734     
48735     this.addEvents({
48736         /**
48737          * @event close
48738          * Fires after the click the close btn
48739              * @param {Roo.form.DisplayField} this
48740              */
48741         close : true
48742     });
48743 };
48744
48745 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48746     inputType:      'hidden',
48747     allowBlank:     true,
48748     readOnly:         true,
48749     
48750  
48751     /**
48752      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48753      */
48754     focusClass : undefined,
48755     /**
48756      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48757      */
48758     fieldClass: 'x-form-field',
48759     
48760      /**
48761      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48762      */
48763     valueRenderer: undefined,
48764     
48765     width: 100,
48766     /**
48767      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48768      * {tag: "input", type: "checkbox", autocomplete: "off"})
48769      */
48770      
48771  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48772  
48773     closable : false,
48774     
48775     onResize : function(){
48776         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48777         
48778     },
48779
48780     initEvents : function(){
48781         // Roo.form.Checkbox.superclass.initEvents.call(this);
48782         // has no events...
48783         
48784         if(this.closable){
48785             this.closeEl.on('click', this.onClose, this);
48786         }
48787        
48788     },
48789
48790
48791     getResizeEl : function(){
48792         return this.wrap;
48793     },
48794
48795     getPositionEl : function(){
48796         return this.wrap;
48797     },
48798
48799     // private
48800     onRender : function(ct, position){
48801         
48802         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48803         //if(this.inputValue !== undefined){
48804         this.wrap = this.el.wrap();
48805         
48806         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48807         
48808         if(this.closable){
48809             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48810         }
48811         
48812         if (this.bodyStyle) {
48813             this.viewEl.applyStyles(this.bodyStyle);
48814         }
48815         //this.viewEl.setStyle('padding', '2px');
48816         
48817         this.setValue(this.value);
48818         
48819     },
48820 /*
48821     // private
48822     initValue : Roo.emptyFn,
48823
48824   */
48825
48826         // private
48827     onClick : function(){
48828         
48829     },
48830
48831     /**
48832      * Sets the checked state of the checkbox.
48833      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48834      */
48835     setValue : function(v){
48836         this.value = v;
48837         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48838         // this might be called before we have a dom element..
48839         if (!this.viewEl) {
48840             return;
48841         }
48842         this.viewEl.dom.innerHTML = html;
48843         Roo.form.DisplayField.superclass.setValue.call(this, v);
48844
48845     },
48846     
48847     onClose : function(e)
48848     {
48849         e.preventDefault();
48850         
48851         this.fireEvent('close', this);
48852     }
48853 });/*
48854  * 
48855  * Licence- LGPL
48856  * 
48857  */
48858
48859 /**
48860  * @class Roo.form.DayPicker
48861  * @extends Roo.form.Field
48862  * A Day picker show [M] [T] [W] ....
48863  * @constructor
48864  * Creates a new Day Picker
48865  * @param {Object} config Configuration options
48866  */
48867 Roo.form.DayPicker= function(config){
48868     Roo.form.DayPicker.superclass.constructor.call(this, config);
48869      
48870 };
48871
48872 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
48873     /**
48874      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48875      */
48876     focusClass : undefined,
48877     /**
48878      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48879      */
48880     fieldClass: "x-form-field",
48881    
48882     /**
48883      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48884      * {tag: "input", type: "checkbox", autocomplete: "off"})
48885      */
48886     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
48887     
48888    
48889     actionMode : 'viewEl', 
48890     //
48891     // private
48892  
48893     inputType : 'hidden',
48894     
48895      
48896     inputElement: false, // real input element?
48897     basedOn: false, // ????
48898     
48899     isFormField: true, // not sure where this is needed!!!!
48900
48901     onResize : function(){
48902         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
48903         if(!this.boxLabel){
48904             this.el.alignTo(this.wrap, 'c-c');
48905         }
48906     },
48907
48908     initEvents : function(){
48909         Roo.form.Checkbox.superclass.initEvents.call(this);
48910         this.el.on("click", this.onClick,  this);
48911         this.el.on("change", this.onClick,  this);
48912     },
48913
48914
48915     getResizeEl : function(){
48916         return this.wrap;
48917     },
48918
48919     getPositionEl : function(){
48920         return this.wrap;
48921     },
48922
48923     
48924     // private
48925     onRender : function(ct, position){
48926         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
48927        
48928         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
48929         
48930         var r1 = '<table><tr>';
48931         var r2 = '<tr class="x-form-daypick-icons">';
48932         for (var i=0; i < 7; i++) {
48933             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
48934             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
48935         }
48936         
48937         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
48938         viewEl.select('img').on('click', this.onClick, this);
48939         this.viewEl = viewEl;   
48940         
48941         
48942         // this will not work on Chrome!!!
48943         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
48944         this.el.on('propertychange', this.setFromHidden,  this);  //ie
48945         
48946         
48947           
48948
48949     },
48950
48951     // private
48952     initValue : Roo.emptyFn,
48953
48954     /**
48955      * Returns the checked state of the checkbox.
48956      * @return {Boolean} True if checked, else false
48957      */
48958     getValue : function(){
48959         return this.el.dom.value;
48960         
48961     },
48962
48963         // private
48964     onClick : function(e){ 
48965         //this.setChecked(!this.checked);
48966         Roo.get(e.target).toggleClass('x-menu-item-checked');
48967         this.refreshValue();
48968         //if(this.el.dom.checked != this.checked){
48969         //    this.setValue(this.el.dom.checked);
48970        // }
48971     },
48972     
48973     // private
48974     refreshValue : function()
48975     {
48976         var val = '';
48977         this.viewEl.select('img',true).each(function(e,i,n)  {
48978             val += e.is(".x-menu-item-checked") ? String(n) : '';
48979         });
48980         this.setValue(val, true);
48981     },
48982
48983     /**
48984      * Sets the checked state of the checkbox.
48985      * On is always based on a string comparison between inputValue and the param.
48986      * @param {Boolean/String} value - the value to set 
48987      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
48988      */
48989     setValue : function(v,suppressEvent){
48990         if (!this.el.dom) {
48991             return;
48992         }
48993         var old = this.el.dom.value ;
48994         this.el.dom.value = v;
48995         if (suppressEvent) {
48996             return ;
48997         }
48998          
48999         // update display..
49000         this.viewEl.select('img',true).each(function(e,i,n)  {
49001             
49002             var on = e.is(".x-menu-item-checked");
49003             var newv = v.indexOf(String(n)) > -1;
49004             if (on != newv) {
49005                 e.toggleClass('x-menu-item-checked');
49006             }
49007             
49008         });
49009         
49010         
49011         this.fireEvent('change', this, v, old);
49012         
49013         
49014     },
49015    
49016     // handle setting of hidden value by some other method!!?!?
49017     setFromHidden: function()
49018     {
49019         if(!this.el){
49020             return;
49021         }
49022         //console.log("SET FROM HIDDEN");
49023         //alert('setFrom hidden');
49024         this.setValue(this.el.dom.value);
49025     },
49026     
49027     onDestroy : function()
49028     {
49029         if(this.viewEl){
49030             Roo.get(this.viewEl).remove();
49031         }
49032          
49033         Roo.form.DayPicker.superclass.onDestroy.call(this);
49034     }
49035
49036 });/*
49037  * RooJS Library 1.1.1
49038  * Copyright(c) 2008-2011  Alan Knowles
49039  *
49040  * License - LGPL
49041  */
49042  
49043
49044 /**
49045  * @class Roo.form.ComboCheck
49046  * @extends Roo.form.ComboBox
49047  * A combobox for multiple select items.
49048  *
49049  * FIXME - could do with a reset button..
49050  * 
49051  * @constructor
49052  * Create a new ComboCheck
49053  * @param {Object} config Configuration options
49054  */
49055 Roo.form.ComboCheck = function(config){
49056     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49057     // should verify some data...
49058     // like
49059     // hiddenName = required..
49060     // displayField = required
49061     // valudField == required
49062     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49063     var _t = this;
49064     Roo.each(req, function(e) {
49065         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49066             throw "Roo.form.ComboCheck : missing value for: " + e;
49067         }
49068     });
49069     
49070     
49071 };
49072
49073 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49074      
49075      
49076     editable : false,
49077      
49078     selectedClass: 'x-menu-item-checked', 
49079     
49080     // private
49081     onRender : function(ct, position){
49082         var _t = this;
49083         
49084         
49085         
49086         if(!this.tpl){
49087             var cls = 'x-combo-list';
49088
49089             
49090             this.tpl =  new Roo.Template({
49091                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49092                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49093                    '<span>{' + this.displayField + '}</span>' +
49094                     '</div>' 
49095                 
49096             });
49097         }
49098  
49099         
49100         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49101         this.view.singleSelect = false;
49102         this.view.multiSelect = true;
49103         this.view.toggleSelect = true;
49104         this.pageTb.add(new Roo.Toolbar.Fill(), {
49105             
49106             text: 'Done',
49107             handler: function()
49108             {
49109                 _t.collapse();
49110             }
49111         });
49112     },
49113     
49114     onViewOver : function(e, t){
49115         // do nothing...
49116         return;
49117         
49118     },
49119     
49120     onViewClick : function(doFocus,index){
49121         return;
49122         
49123     },
49124     select: function () {
49125         //Roo.log("SELECT CALLED");
49126     },
49127      
49128     selectByValue : function(xv, scrollIntoView){
49129         var ar = this.getValueArray();
49130         var sels = [];
49131         
49132         Roo.each(ar, function(v) {
49133             if(v === undefined || v === null){
49134                 return;
49135             }
49136             var r = this.findRecord(this.valueField, v);
49137             if(r){
49138                 sels.push(this.store.indexOf(r))
49139                 
49140             }
49141         },this);
49142         this.view.select(sels);
49143         return false;
49144     },
49145     
49146     
49147     
49148     onSelect : function(record, index){
49149        // Roo.log("onselect Called");
49150        // this is only called by the clear button now..
49151         this.view.clearSelections();
49152         this.setValue('[]');
49153         if (this.value != this.valueBefore) {
49154             this.fireEvent('change', this, this.value, this.valueBefore);
49155             this.valueBefore = this.value;
49156         }
49157     },
49158     getValueArray : function()
49159     {
49160         var ar = [] ;
49161         
49162         try {
49163             //Roo.log(this.value);
49164             if (typeof(this.value) == 'undefined') {
49165                 return [];
49166             }
49167             var ar = Roo.decode(this.value);
49168             return  ar instanceof Array ? ar : []; //?? valid?
49169             
49170         } catch(e) {
49171             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49172             return [];
49173         }
49174          
49175     },
49176     expand : function ()
49177     {
49178         
49179         Roo.form.ComboCheck.superclass.expand.call(this);
49180         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49181         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49182         
49183
49184     },
49185     
49186     collapse : function(){
49187         Roo.form.ComboCheck.superclass.collapse.call(this);
49188         var sl = this.view.getSelectedIndexes();
49189         var st = this.store;
49190         var nv = [];
49191         var tv = [];
49192         var r;
49193         Roo.each(sl, function(i) {
49194             r = st.getAt(i);
49195             nv.push(r.get(this.valueField));
49196         },this);
49197         this.setValue(Roo.encode(nv));
49198         if (this.value != this.valueBefore) {
49199
49200             this.fireEvent('change', this, this.value, this.valueBefore);
49201             this.valueBefore = this.value;
49202         }
49203         
49204     },
49205     
49206     setValue : function(v){
49207         // Roo.log(v);
49208         this.value = v;
49209         
49210         var vals = this.getValueArray();
49211         var tv = [];
49212         Roo.each(vals, function(k) {
49213             var r = this.findRecord(this.valueField, k);
49214             if(r){
49215                 tv.push(r.data[this.displayField]);
49216             }else if(this.valueNotFoundText !== undefined){
49217                 tv.push( this.valueNotFoundText );
49218             }
49219         },this);
49220        // Roo.log(tv);
49221         
49222         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49223         this.hiddenField.value = v;
49224         this.value = v;
49225     }
49226     
49227 });/*
49228  * Based on:
49229  * Ext JS Library 1.1.1
49230  * Copyright(c) 2006-2007, Ext JS, LLC.
49231  *
49232  * Originally Released Under LGPL - original licence link has changed is not relivant.
49233  *
49234  * Fork - LGPL
49235  * <script type="text/javascript">
49236  */
49237  
49238 /**
49239  * @class Roo.form.Signature
49240  * @extends Roo.form.Field
49241  * Signature field.  
49242  * @constructor
49243  * 
49244  * @param {Object} config Configuration options
49245  */
49246
49247 Roo.form.Signature = function(config){
49248     Roo.form.Signature.superclass.constructor.call(this, config);
49249     
49250     this.addEvents({// not in used??
49251          /**
49252          * @event confirm
49253          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49254              * @param {Roo.form.Signature} combo This combo box
49255              */
49256         'confirm' : true,
49257         /**
49258          * @event reset
49259          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49260              * @param {Roo.form.ComboBox} combo This combo box
49261              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49262              */
49263         'reset' : true
49264     });
49265 };
49266
49267 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49268     /**
49269      * @cfg {Object} labels Label to use when rendering a form.
49270      * defaults to 
49271      * labels : { 
49272      *      clear : "Clear",
49273      *      confirm : "Confirm"
49274      *  }
49275      */
49276     labels : { 
49277         clear : "Clear",
49278         confirm : "Confirm"
49279     },
49280     /**
49281      * @cfg {Number} width The signature panel width (defaults to 300)
49282      */
49283     width: 300,
49284     /**
49285      * @cfg {Number} height The signature panel height (defaults to 100)
49286      */
49287     height : 100,
49288     /**
49289      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49290      */
49291     allowBlank : false,
49292     
49293     //private
49294     // {Object} signPanel The signature SVG panel element (defaults to {})
49295     signPanel : {},
49296     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49297     isMouseDown : false,
49298     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49299     isConfirmed : false,
49300     // {String} signatureTmp SVG mapping string (defaults to empty string)
49301     signatureTmp : '',
49302     
49303     
49304     defaultAutoCreate : { // modified by initCompnoent..
49305         tag: "input",
49306         type:"hidden"
49307     },
49308
49309     // private
49310     onRender : function(ct, position){
49311         
49312         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49313         
49314         this.wrap = this.el.wrap({
49315             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49316         });
49317         
49318         this.createToolbar(this);
49319         this.signPanel = this.wrap.createChild({
49320                 tag: 'div',
49321                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49322             }, this.el
49323         );
49324             
49325         this.svgID = Roo.id();
49326         this.svgEl = this.signPanel.createChild({
49327               xmlns : 'http://www.w3.org/2000/svg',
49328               tag : 'svg',
49329               id : this.svgID + "-svg",
49330               width: this.width,
49331               height: this.height,
49332               viewBox: '0 0 '+this.width+' '+this.height,
49333               cn : [
49334                 {
49335                     tag: "rect",
49336                     id: this.svgID + "-svg-r",
49337                     width: this.width,
49338                     height: this.height,
49339                     fill: "#ffa"
49340                 },
49341                 {
49342                     tag: "line",
49343                     id: this.svgID + "-svg-l",
49344                     x1: "0", // start
49345                     y1: (this.height*0.8), // start set the line in 80% of height
49346                     x2: this.width, // end
49347                     y2: (this.height*0.8), // end set the line in 80% of height
49348                     'stroke': "#666",
49349                     'stroke-width': "1",
49350                     'stroke-dasharray': "3",
49351                     'shape-rendering': "crispEdges",
49352                     'pointer-events': "none"
49353                 },
49354                 {
49355                     tag: "path",
49356                     id: this.svgID + "-svg-p",
49357                     'stroke': "navy",
49358                     'stroke-width': "3",
49359                     'fill': "none",
49360                     'pointer-events': 'none'
49361                 }
49362               ]
49363         });
49364         this.createSVG();
49365         this.svgBox = this.svgEl.dom.getScreenCTM();
49366     },
49367     createSVG : function(){ 
49368         var svg = this.signPanel;
49369         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49370         var t = this;
49371
49372         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49373         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49374         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49375         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49376         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49377         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49378         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49379         
49380     },
49381     isTouchEvent : function(e){
49382         return e.type.match(/^touch/);
49383     },
49384     getCoords : function (e) {
49385         var pt    = this.svgEl.dom.createSVGPoint();
49386         pt.x = e.clientX; 
49387         pt.y = e.clientY;
49388         if (this.isTouchEvent(e)) {
49389             pt.x =  e.targetTouches[0].clientX;
49390             pt.y = e.targetTouches[0].clientY;
49391         }
49392         var a = this.svgEl.dom.getScreenCTM();
49393         var b = a.inverse();
49394         var mx = pt.matrixTransform(b);
49395         return mx.x + ',' + mx.y;
49396     },
49397     //mouse event headler 
49398     down : function (e) {
49399         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49400         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49401         
49402         this.isMouseDown = true;
49403         
49404         e.preventDefault();
49405     },
49406     move : function (e) {
49407         if (this.isMouseDown) {
49408             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49409             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49410         }
49411         
49412         e.preventDefault();
49413     },
49414     up : function (e) {
49415         this.isMouseDown = false;
49416         var sp = this.signatureTmp.split(' ');
49417         
49418         if(sp.length > 1){
49419             if(!sp[sp.length-2].match(/^L/)){
49420                 sp.pop();
49421                 sp.pop();
49422                 sp.push("");
49423                 this.signatureTmp = sp.join(" ");
49424             }
49425         }
49426         if(this.getValue() != this.signatureTmp){
49427             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49428             this.isConfirmed = false;
49429         }
49430         e.preventDefault();
49431     },
49432     
49433     /**
49434      * Protected method that will not generally be called directly. It
49435      * is called when the editor creates its toolbar. Override this method if you need to
49436      * add custom toolbar buttons.
49437      * @param {HtmlEditor} editor
49438      */
49439     createToolbar : function(editor){
49440          function btn(id, toggle, handler){
49441             var xid = fid + '-'+ id ;
49442             return {
49443                 id : xid,
49444                 cmd : id,
49445                 cls : 'x-btn-icon x-edit-'+id,
49446                 enableToggle:toggle !== false,
49447                 scope: editor, // was editor...
49448                 handler:handler||editor.relayBtnCmd,
49449                 clickEvent:'mousedown',
49450                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49451                 tabIndex:-1
49452             };
49453         }
49454         
49455         
49456         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49457         this.tb = tb;
49458         this.tb.add(
49459            {
49460                 cls : ' x-signature-btn x-signature-'+id,
49461                 scope: editor, // was editor...
49462                 handler: this.reset,
49463                 clickEvent:'mousedown',
49464                 text: this.labels.clear
49465             },
49466             {
49467                  xtype : 'Fill',
49468                  xns: Roo.Toolbar
49469             }, 
49470             {
49471                 cls : '  x-signature-btn x-signature-'+id,
49472                 scope: editor, // was editor...
49473                 handler: this.confirmHandler,
49474                 clickEvent:'mousedown',
49475                 text: this.labels.confirm
49476             }
49477         );
49478     
49479     },
49480     //public
49481     /**
49482      * when user is clicked confirm then show this image.....
49483      * 
49484      * @return {String} Image Data URI
49485      */
49486     getImageDataURI : function(){
49487         var svg = this.svgEl.dom.parentNode.innerHTML;
49488         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49489         return src; 
49490     },
49491     /**
49492      * 
49493      * @return {Boolean} this.isConfirmed
49494      */
49495     getConfirmed : function(){
49496         return this.isConfirmed;
49497     },
49498     /**
49499      * 
49500      * @return {Number} this.width
49501      */
49502     getWidth : function(){
49503         return this.width;
49504     },
49505     /**
49506      * 
49507      * @return {Number} this.height
49508      */
49509     getHeight : function(){
49510         return this.height;
49511     },
49512     // private
49513     getSignature : function(){
49514         return this.signatureTmp;
49515     },
49516     // private
49517     reset : function(){
49518         this.signatureTmp = '';
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', '');
49521         this.isConfirmed = false;
49522         Roo.form.Signature.superclass.reset.call(this);
49523     },
49524     setSignature : function(s){
49525         this.signatureTmp = s;
49526         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49527         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49528         this.setValue(s);
49529         this.isConfirmed = false;
49530         Roo.form.Signature.superclass.reset.call(this);
49531     }, 
49532     test : function(){
49533 //        Roo.log(this.signPanel.dom.contentWindow.up())
49534     },
49535     //private
49536     setConfirmed : function(){
49537         
49538         
49539         
49540 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49541     },
49542     // private
49543     confirmHandler : function(){
49544         if(!this.getSignature()){
49545             return;
49546         }
49547         
49548         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49549         this.setValue(this.getSignature());
49550         this.isConfirmed = true;
49551         
49552         this.fireEvent('confirm', this);
49553     },
49554     // private
49555     // Subclasses should provide the validation implementation by overriding this
49556     validateValue : function(value){
49557         if(this.allowBlank){
49558             return true;
49559         }
49560         
49561         if(this.isConfirmed){
49562             return true;
49563         }
49564         return false;
49565     }
49566 });/*
49567  * Based on:
49568  * Ext JS Library 1.1.1
49569  * Copyright(c) 2006-2007, Ext JS, LLC.
49570  *
49571  * Originally Released Under LGPL - original licence link has changed is not relivant.
49572  *
49573  * Fork - LGPL
49574  * <script type="text/javascript">
49575  */
49576  
49577
49578 /**
49579  * @class Roo.form.ComboBox
49580  * @extends Roo.form.TriggerField
49581  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49582  * @constructor
49583  * Create a new ComboBox.
49584  * @param {Object} config Configuration options
49585  */
49586 Roo.form.Select = function(config){
49587     Roo.form.Select.superclass.constructor.call(this, config);
49588      
49589 };
49590
49591 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49592     /**
49593      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49594      */
49595     /**
49596      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49597      * rendering into an Roo.Editor, defaults to false)
49598      */
49599     /**
49600      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49601      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49602      */
49603     /**
49604      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49605      */
49606     /**
49607      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49608      * the dropdown list (defaults to undefined, with no header element)
49609      */
49610
49611      /**
49612      * @cfg {String/Roo.Template} tpl The template to use to render the output
49613      */
49614      
49615     // private
49616     defaultAutoCreate : {tag: "select"  },
49617     /**
49618      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49619      */
49620     listWidth: undefined,
49621     /**
49622      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49623      * mode = 'remote' or 'text' if mode = 'local')
49624      */
49625     displayField: undefined,
49626     /**
49627      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49628      * mode = 'remote' or 'value' if mode = 'local'). 
49629      * Note: use of a valueField requires the user make a selection
49630      * in order for a value to be mapped.
49631      */
49632     valueField: undefined,
49633     
49634     
49635     /**
49636      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49637      * field's data value (defaults to the underlying DOM element's name)
49638      */
49639     hiddenName: undefined,
49640     /**
49641      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49642      */
49643     listClass: '',
49644     /**
49645      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49646      */
49647     selectedClass: 'x-combo-selected',
49648     /**
49649      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49650      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49651      * which displays a downward arrow icon).
49652      */
49653     triggerClass : 'x-form-arrow-trigger',
49654     /**
49655      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49656      */
49657     shadow:'sides',
49658     /**
49659      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49660      * anchor positions (defaults to 'tl-bl')
49661      */
49662     listAlign: 'tl-bl?',
49663     /**
49664      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49665      */
49666     maxHeight: 300,
49667     /**
49668      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49669      * query specified by the allQuery config option (defaults to 'query')
49670      */
49671     triggerAction: 'query',
49672     /**
49673      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49674      * (defaults to 4, does not apply if editable = false)
49675      */
49676     minChars : 4,
49677     /**
49678      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49679      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49680      */
49681     typeAhead: false,
49682     /**
49683      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49684      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49685      */
49686     queryDelay: 500,
49687     /**
49688      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49689      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49690      */
49691     pageSize: 0,
49692     /**
49693      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49694      * when editable = true (defaults to false)
49695      */
49696     selectOnFocus:false,
49697     /**
49698      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49699      */
49700     queryParam: 'query',
49701     /**
49702      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49703      * when mode = 'remote' (defaults to 'Loading...')
49704      */
49705     loadingText: 'Loading...',
49706     /**
49707      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49708      */
49709     resizable: false,
49710     /**
49711      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49712      */
49713     handleHeight : 8,
49714     /**
49715      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49716      * traditional select (defaults to true)
49717      */
49718     editable: true,
49719     /**
49720      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49721      */
49722     allQuery: '',
49723     /**
49724      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49725      */
49726     mode: 'remote',
49727     /**
49728      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49729      * listWidth has a higher value)
49730      */
49731     minListWidth : 70,
49732     /**
49733      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49734      * allow the user to set arbitrary text into the field (defaults to false)
49735      */
49736     forceSelection:false,
49737     /**
49738      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49739      * if typeAhead = true (defaults to 250)
49740      */
49741     typeAheadDelay : 250,
49742     /**
49743      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49744      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49745      */
49746     valueNotFoundText : undefined,
49747     
49748     /**
49749      * @cfg {String} defaultValue The value displayed after loading the store.
49750      */
49751     defaultValue: '',
49752     
49753     /**
49754      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49755      */
49756     blockFocus : false,
49757     
49758     /**
49759      * @cfg {Boolean} disableClear Disable showing of clear button.
49760      */
49761     disableClear : false,
49762     /**
49763      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49764      */
49765     alwaysQuery : false,
49766     
49767     //private
49768     addicon : false,
49769     editicon: false,
49770     
49771     // element that contains real text value.. (when hidden is used..)
49772      
49773     // private
49774     onRender : function(ct, position){
49775         Roo.form.Field.prototype.onRender.call(this, ct, position);
49776         
49777         if(this.store){
49778             this.store.on('beforeload', this.onBeforeLoad, this);
49779             this.store.on('load', this.onLoad, this);
49780             this.store.on('loadexception', this.onLoadException, this);
49781             this.store.load({});
49782         }
49783         
49784         
49785         
49786     },
49787
49788     // private
49789     initEvents : function(){
49790         //Roo.form.ComboBox.superclass.initEvents.call(this);
49791  
49792     },
49793
49794     onDestroy : function(){
49795        
49796         if(this.store){
49797             this.store.un('beforeload', this.onBeforeLoad, this);
49798             this.store.un('load', this.onLoad, this);
49799             this.store.un('loadexception', this.onLoadException, this);
49800         }
49801         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49802     },
49803
49804     // private
49805     fireKey : function(e){
49806         if(e.isNavKeyPress() && !this.list.isVisible()){
49807             this.fireEvent("specialkey", this, e);
49808         }
49809     },
49810
49811     // private
49812     onResize: function(w, h){
49813         
49814         return; 
49815     
49816         
49817     },
49818
49819     /**
49820      * Allow or prevent the user from directly editing the field text.  If false is passed,
49821      * the user will only be able to select from the items defined in the dropdown list.  This method
49822      * is the runtime equivalent of setting the 'editable' config option at config time.
49823      * @param {Boolean} value True to allow the user to directly edit the field text
49824      */
49825     setEditable : function(value){
49826          
49827     },
49828
49829     // private
49830     onBeforeLoad : function(){
49831         
49832         Roo.log("Select before load");
49833         return;
49834     
49835         this.innerList.update(this.loadingText ?
49836                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49837         //this.restrictHeight();
49838         this.selectedIndex = -1;
49839     },
49840
49841     // private
49842     onLoad : function(){
49843
49844     
49845         var dom = this.el.dom;
49846         dom.innerHTML = '';
49847          var od = dom.ownerDocument;
49848          
49849         if (this.emptyText) {
49850             var op = od.createElement('option');
49851             op.setAttribute('value', '');
49852             op.innerHTML = String.format('{0}', this.emptyText);
49853             dom.appendChild(op);
49854         }
49855         if(this.store.getCount() > 0){
49856            
49857             var vf = this.valueField;
49858             var df = this.displayField;
49859             this.store.data.each(function(r) {
49860                 // which colmsn to use... testing - cdoe / title..
49861                 var op = od.createElement('option');
49862                 op.setAttribute('value', r.data[vf]);
49863                 op.innerHTML = String.format('{0}', r.data[df]);
49864                 dom.appendChild(op);
49865             });
49866             if (typeof(this.defaultValue != 'undefined')) {
49867                 this.setValue(this.defaultValue);
49868             }
49869             
49870              
49871         }else{
49872             //this.onEmptyResults();
49873         }
49874         //this.el.focus();
49875     },
49876     // private
49877     onLoadException : function()
49878     {
49879         dom.innerHTML = '';
49880             
49881         Roo.log("Select on load exception");
49882         return;
49883     
49884         this.collapse();
49885         Roo.log(this.store.reader.jsonData);
49886         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
49887             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
49888         }
49889         
49890         
49891     },
49892     // private
49893     onTypeAhead : function(){
49894          
49895     },
49896
49897     // private
49898     onSelect : function(record, index){
49899         Roo.log('on select?');
49900         return;
49901         if(this.fireEvent('beforeselect', this, record, index) !== false){
49902             this.setFromData(index > -1 ? record.data : false);
49903             this.collapse();
49904             this.fireEvent('select', this, record, index);
49905         }
49906     },
49907
49908     /**
49909      * Returns the currently selected field value or empty string if no value is set.
49910      * @return {String} value The selected value
49911      */
49912     getValue : function(){
49913         var dom = this.el.dom;
49914         this.value = dom.options[dom.selectedIndex].value;
49915         return this.value;
49916         
49917     },
49918
49919     /**
49920      * Clears any text/value currently set in the field
49921      */
49922     clearValue : function(){
49923         this.value = '';
49924         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
49925         
49926     },
49927
49928     /**
49929      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
49930      * will be displayed in the field.  If the value does not match the data value of an existing item,
49931      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
49932      * Otherwise the field will be blank (although the value will still be set).
49933      * @param {String} value The value to match
49934      */
49935     setValue : function(v){
49936         var d = this.el.dom;
49937         for (var i =0; i < d.options.length;i++) {
49938             if (v == d.options[i].value) {
49939                 d.selectedIndex = i;
49940                 this.value = v;
49941                 return;
49942             }
49943         }
49944         this.clearValue();
49945     },
49946     /**
49947      * @property {Object} the last set data for the element
49948      */
49949     
49950     lastData : false,
49951     /**
49952      * Sets the value of the field based on a object which is related to the record format for the store.
49953      * @param {Object} value the value to set as. or false on reset?
49954      */
49955     setFromData : function(o){
49956         Roo.log('setfrom data?');
49957          
49958         
49959         
49960     },
49961     // private
49962     reset : function(){
49963         this.clearValue();
49964     },
49965     // private
49966     findRecord : function(prop, value){
49967         
49968         return false;
49969     
49970         var record;
49971         if(this.store.getCount() > 0){
49972             this.store.each(function(r){
49973                 if(r.data[prop] == value){
49974                     record = r;
49975                     return false;
49976                 }
49977                 return true;
49978             });
49979         }
49980         return record;
49981     },
49982     
49983     getName: function()
49984     {
49985         // returns hidden if it's set..
49986         if (!this.rendered) {return ''};
49987         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
49988         
49989     },
49990      
49991
49992     
49993
49994     // private
49995     onEmptyResults : function(){
49996         Roo.log('empty results');
49997         //this.collapse();
49998     },
49999
50000     /**
50001      * Returns true if the dropdown list is expanded, else false.
50002      */
50003     isExpanded : function(){
50004         return false;
50005     },
50006
50007     /**
50008      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50009      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50010      * @param {String} value The data value of the item to select
50011      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50012      * selected item if it is not currently in view (defaults to true)
50013      * @return {Boolean} True if the value matched an item in the list, else false
50014      */
50015     selectByValue : function(v, scrollIntoView){
50016         Roo.log('select By Value');
50017         return false;
50018     
50019         if(v !== undefined && v !== null){
50020             var r = this.findRecord(this.valueField || this.displayField, v);
50021             if(r){
50022                 this.select(this.store.indexOf(r), scrollIntoView);
50023                 return true;
50024             }
50025         }
50026         return false;
50027     },
50028
50029     /**
50030      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50031      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50032      * @param {Number} index The zero-based index of the list item to select
50033      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50034      * selected item if it is not currently in view (defaults to true)
50035      */
50036     select : function(index, scrollIntoView){
50037         Roo.log('select ');
50038         return  ;
50039         
50040         this.selectedIndex = index;
50041         this.view.select(index);
50042         if(scrollIntoView !== false){
50043             var el = this.view.getNode(index);
50044             if(el){
50045                 this.innerList.scrollChildIntoView(el, false);
50046             }
50047         }
50048     },
50049
50050       
50051
50052     // private
50053     validateBlur : function(){
50054         
50055         return;
50056         
50057     },
50058
50059     // private
50060     initQuery : function(){
50061         this.doQuery(this.getRawValue());
50062     },
50063
50064     // private
50065     doForce : function(){
50066         if(this.el.dom.value.length > 0){
50067             this.el.dom.value =
50068                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50069              
50070         }
50071     },
50072
50073     /**
50074      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50075      * query allowing the query action to be canceled if needed.
50076      * @param {String} query The SQL query to execute
50077      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50078      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50079      * saved in the current store (defaults to false)
50080      */
50081     doQuery : function(q, forceAll){
50082         
50083         Roo.log('doQuery?');
50084         if(q === undefined || q === null){
50085             q = '';
50086         }
50087         var qe = {
50088             query: q,
50089             forceAll: forceAll,
50090             combo: this,
50091             cancel:false
50092         };
50093         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50094             return false;
50095         }
50096         q = qe.query;
50097         forceAll = qe.forceAll;
50098         if(forceAll === true || (q.length >= this.minChars)){
50099             if(this.lastQuery != q || this.alwaysQuery){
50100                 this.lastQuery = q;
50101                 if(this.mode == 'local'){
50102                     this.selectedIndex = -1;
50103                     if(forceAll){
50104                         this.store.clearFilter();
50105                     }else{
50106                         this.store.filter(this.displayField, q);
50107                     }
50108                     this.onLoad();
50109                 }else{
50110                     this.store.baseParams[this.queryParam] = q;
50111                     this.store.load({
50112                         params: this.getParams(q)
50113                     });
50114                     this.expand();
50115                 }
50116             }else{
50117                 this.selectedIndex = -1;
50118                 this.onLoad();   
50119             }
50120         }
50121     },
50122
50123     // private
50124     getParams : function(q){
50125         var p = {};
50126         //p[this.queryParam] = q;
50127         if(this.pageSize){
50128             p.start = 0;
50129             p.limit = this.pageSize;
50130         }
50131         return p;
50132     },
50133
50134     /**
50135      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50136      */
50137     collapse : function(){
50138         
50139     },
50140
50141     // private
50142     collapseIf : function(e){
50143         
50144     },
50145
50146     /**
50147      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50148      */
50149     expand : function(){
50150         
50151     } ,
50152
50153     // private
50154      
50155
50156     /** 
50157     * @cfg {Boolean} grow 
50158     * @hide 
50159     */
50160     /** 
50161     * @cfg {Number} growMin 
50162     * @hide 
50163     */
50164     /** 
50165     * @cfg {Number} growMax 
50166     * @hide 
50167     */
50168     /**
50169      * @hide
50170      * @method autoSize
50171      */
50172     
50173     setWidth : function()
50174     {
50175         
50176     },
50177     getResizeEl : function(){
50178         return this.el;
50179     }
50180 });//<script type="text/javasscript">
50181  
50182
50183 /**
50184  * @class Roo.DDView
50185  * A DnD enabled version of Roo.View.
50186  * @param {Element/String} container The Element in which to create the View.
50187  * @param {String} tpl The template string used to create the markup for each element of the View
50188  * @param {Object} config The configuration properties. These include all the config options of
50189  * {@link Roo.View} plus some specific to this class.<br>
50190  * <p>
50191  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50192  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50193  * <p>
50194  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50195 .x-view-drag-insert-above {
50196         border-top:1px dotted #3366cc;
50197 }
50198 .x-view-drag-insert-below {
50199         border-bottom:1px dotted #3366cc;
50200 }
50201 </code></pre>
50202  * 
50203  */
50204  
50205 Roo.DDView = function(container, tpl, config) {
50206     Roo.DDView.superclass.constructor.apply(this, arguments);
50207     this.getEl().setStyle("outline", "0px none");
50208     this.getEl().unselectable();
50209     if (this.dragGroup) {
50210                 this.setDraggable(this.dragGroup.split(","));
50211     }
50212     if (this.dropGroup) {
50213                 this.setDroppable(this.dropGroup.split(","));
50214     }
50215     if (this.deletable) {
50216         this.setDeletable();
50217     }
50218     this.isDirtyFlag = false;
50219         this.addEvents({
50220                 "drop" : true
50221         });
50222 };
50223
50224 Roo.extend(Roo.DDView, Roo.View, {
50225 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50226 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50227 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50228 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50229
50230         isFormField: true,
50231
50232         reset: Roo.emptyFn,
50233         
50234         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50235
50236         validate: function() {
50237                 return true;
50238         },
50239         
50240         destroy: function() {
50241                 this.purgeListeners();
50242                 this.getEl.removeAllListeners();
50243                 this.getEl().remove();
50244                 if (this.dragZone) {
50245                         if (this.dragZone.destroy) {
50246                                 this.dragZone.destroy();
50247                         }
50248                 }
50249                 if (this.dropZone) {
50250                         if (this.dropZone.destroy) {
50251                                 this.dropZone.destroy();
50252                         }
50253                 }
50254         },
50255
50256 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50257         getName: function() {
50258                 return this.name;
50259         },
50260
50261 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50262         setValue: function(v) {
50263                 if (!this.store) {
50264                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50265                 }
50266                 var data = {};
50267                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50268                 this.store.proxy = new Roo.data.MemoryProxy(data);
50269                 this.store.load();
50270         },
50271
50272 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50273         getValue: function() {
50274                 var result = '(';
50275                 this.store.each(function(rec) {
50276                         result += rec.id + ',';
50277                 });
50278                 return result.substr(0, result.length - 1) + ')';
50279         },
50280         
50281         getIds: function() {
50282                 var i = 0, result = new Array(this.store.getCount());
50283                 this.store.each(function(rec) {
50284                         result[i++] = rec.id;
50285                 });
50286                 return result;
50287         },
50288         
50289         isDirty: function() {
50290                 return this.isDirtyFlag;
50291         },
50292
50293 /**
50294  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50295  *      whole Element becomes the target, and this causes the drop gesture to append.
50296  */
50297     getTargetFromEvent : function(e) {
50298                 var target = e.getTarget();
50299                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50300                 target = target.parentNode;
50301                 }
50302                 if (!target) {
50303                         target = this.el.dom.lastChild || this.el.dom;
50304                 }
50305                 return target;
50306     },
50307
50308 /**
50309  *      Create the drag data which consists of an object which has the property "ddel" as
50310  *      the drag proxy element. 
50311  */
50312     getDragData : function(e) {
50313         var target = this.findItemFromChild(e.getTarget());
50314                 if(target) {
50315                         this.handleSelection(e);
50316                         var selNodes = this.getSelectedNodes();
50317             var dragData = {
50318                 source: this,
50319                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50320                 nodes: selNodes,
50321                 records: []
50322                         };
50323                         var selectedIndices = this.getSelectedIndexes();
50324                         for (var i = 0; i < selectedIndices.length; i++) {
50325                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50326                         }
50327                         if (selNodes.length == 1) {
50328                                 dragData.ddel = target.cloneNode(true); // the div element
50329                         } else {
50330                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50331                                 div.className = 'multi-proxy';
50332                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50333                                         div.appendChild(selNodes[i].cloneNode(true));
50334                                 }
50335                                 dragData.ddel = div;
50336                         }
50337             //console.log(dragData)
50338             //console.log(dragData.ddel.innerHTML)
50339                         return dragData;
50340                 }
50341         //console.log('nodragData')
50342                 return false;
50343     },
50344     
50345 /**     Specify to which ddGroup items in this DDView may be dragged. */
50346     setDraggable: function(ddGroup) {
50347         if (ddGroup instanceof Array) {
50348                 Roo.each(ddGroup, this.setDraggable, this);
50349                 return;
50350         }
50351         if (this.dragZone) {
50352                 this.dragZone.addToGroup(ddGroup);
50353         } else {
50354                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50355                                 containerScroll: true,
50356                                 ddGroup: ddGroup 
50357
50358                         });
50359 //                      Draggability implies selection. DragZone's mousedown selects the element.
50360                         if (!this.multiSelect) { this.singleSelect = true; }
50361
50362 //                      Wire the DragZone's handlers up to methods in *this*
50363                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50364                 }
50365     },
50366
50367 /**     Specify from which ddGroup this DDView accepts drops. */
50368     setDroppable: function(ddGroup) {
50369         if (ddGroup instanceof Array) {
50370                 Roo.each(ddGroup, this.setDroppable, this);
50371                 return;
50372         }
50373         if (this.dropZone) {
50374                 this.dropZone.addToGroup(ddGroup);
50375         } else {
50376                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50377                                 containerScroll: true,
50378                                 ddGroup: ddGroup
50379                         });
50380
50381 //                      Wire the DropZone's handlers up to methods in *this*
50382                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50383                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50384                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50385                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50386                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50387                 }
50388     },
50389
50390 /**     Decide whether to drop above or below a View node. */
50391     getDropPoint : function(e, n, dd){
50392         if (n == this.el.dom) { return "above"; }
50393                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50394                 var c = t + (b - t) / 2;
50395                 var y = Roo.lib.Event.getPageY(e);
50396                 if(y <= c) {
50397                         return "above";
50398                 }else{
50399                         return "below";
50400                 }
50401     },
50402
50403     onNodeEnter : function(n, dd, e, data){
50404                 return false;
50405     },
50406     
50407     onNodeOver : function(n, dd, e, data){
50408                 var pt = this.getDropPoint(e, n, dd);
50409                 // set the insert point style on the target node
50410                 var dragElClass = this.dropNotAllowed;
50411                 if (pt) {
50412                         var targetElClass;
50413                         if (pt == "above"){
50414                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50415                                 targetElClass = "x-view-drag-insert-above";
50416                         } else {
50417                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50418                                 targetElClass = "x-view-drag-insert-below";
50419                         }
50420                         if (this.lastInsertClass != targetElClass){
50421                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50422                                 this.lastInsertClass = targetElClass;
50423                         }
50424                 }
50425                 return dragElClass;
50426         },
50427
50428     onNodeOut : function(n, dd, e, data){
50429                 this.removeDropIndicators(n);
50430     },
50431
50432     onNodeDrop : function(n, dd, e, data){
50433         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50434                 return false;
50435         }
50436         var pt = this.getDropPoint(e, n, dd);
50437                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50438                 if (pt == "below") { insertAt++; }
50439                 for (var i = 0; i < data.records.length; i++) {
50440                         var r = data.records[i];
50441                         var dup = this.store.getById(r.id);
50442                         if (dup && (dd != this.dragZone)) {
50443                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50444                         } else {
50445                                 if (data.copy) {
50446                                         this.store.insert(insertAt++, r.copy());
50447                                 } else {
50448                                         data.source.isDirtyFlag = true;
50449                                         r.store.remove(r);
50450                                         this.store.insert(insertAt++, r);
50451                                 }
50452                                 this.isDirtyFlag = true;
50453                         }
50454                 }
50455                 this.dragZone.cachedTarget = null;
50456                 return true;
50457     },
50458
50459     removeDropIndicators : function(n){
50460                 if(n){
50461                         Roo.fly(n).removeClass([
50462                                 "x-view-drag-insert-above",
50463                                 "x-view-drag-insert-below"]);
50464                         this.lastInsertClass = "_noclass";
50465                 }
50466     },
50467
50468 /**
50469  *      Utility method. Add a delete option to the DDView's context menu.
50470  *      @param {String} imageUrl The URL of the "delete" icon image.
50471  */
50472         setDeletable: function(imageUrl) {
50473                 if (!this.singleSelect && !this.multiSelect) {
50474                         this.singleSelect = true;
50475                 }
50476                 var c = this.getContextMenu();
50477                 this.contextMenu.on("itemclick", function(item) {
50478                         switch (item.id) {
50479                                 case "delete":
50480                                         this.remove(this.getSelectedIndexes());
50481                                         break;
50482                         }
50483                 }, this);
50484                 this.contextMenu.add({
50485                         icon: imageUrl,
50486                         id: "delete",
50487                         text: 'Delete'
50488                 });
50489         },
50490         
50491 /**     Return the context menu for this DDView. */
50492         getContextMenu: function() {
50493                 if (!this.contextMenu) {
50494 //                      Create the View's context menu
50495                         this.contextMenu = new Roo.menu.Menu({
50496                                 id: this.id + "-contextmenu"
50497                         });
50498                         this.el.on("contextmenu", this.showContextMenu, this);
50499                 }
50500                 return this.contextMenu;
50501         },
50502         
50503         disableContextMenu: function() {
50504                 if (this.contextMenu) {
50505                         this.el.un("contextmenu", this.showContextMenu, this);
50506                 }
50507         },
50508
50509         showContextMenu: function(e, item) {
50510         item = this.findItemFromChild(e.getTarget());
50511                 if (item) {
50512                         e.stopEvent();
50513                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50514                         this.contextMenu.showAt(e.getXY());
50515             }
50516     },
50517
50518 /**
50519  *      Remove {@link Roo.data.Record}s at the specified indices.
50520  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50521  */
50522     remove: function(selectedIndices) {
50523                 selectedIndices = [].concat(selectedIndices);
50524                 for (var i = 0; i < selectedIndices.length; i++) {
50525                         var rec = this.store.getAt(selectedIndices[i]);
50526                         this.store.remove(rec);
50527                 }
50528     },
50529
50530 /**
50531  *      Double click fires the event, but also, if this is draggable, and there is only one other
50532  *      related DropZone, it transfers the selected node.
50533  */
50534     onDblClick : function(e){
50535         var item = this.findItemFromChild(e.getTarget());
50536         if(item){
50537             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50538                 return false;
50539             }
50540             if (this.dragGroup) {
50541                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50542                     while (targets.indexOf(this.dropZone) > -1) {
50543                             targets.remove(this.dropZone);
50544                                 }
50545                     if (targets.length == 1) {
50546                                         this.dragZone.cachedTarget = null;
50547                         var el = Roo.get(targets[0].getEl());
50548                         var box = el.getBox(true);
50549                         targets[0].onNodeDrop(el.dom, {
50550                                 target: el.dom,
50551                                 xy: [box.x, box.y + box.height - 1]
50552                         }, null, this.getDragData(e));
50553                     }
50554                 }
50555         }
50556     },
50557     
50558     handleSelection: function(e) {
50559                 this.dragZone.cachedTarget = null;
50560         var item = this.findItemFromChild(e.getTarget());
50561         if (!item) {
50562                 this.clearSelections(true);
50563                 return;
50564         }
50565                 if (item && (this.multiSelect || this.singleSelect)){
50566                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50567                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50568                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50569                                 this.unselect(item);
50570                         } else {
50571                                 this.select(item, this.multiSelect && e.ctrlKey);
50572                                 this.lastSelection = item;
50573                         }
50574                 }
50575     },
50576
50577     onItemClick : function(item, index, e){
50578                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50579                         return false;
50580                 }
50581                 return true;
50582     },
50583
50584     unselect : function(nodeInfo, suppressEvent){
50585                 var node = this.getNode(nodeInfo);
50586                 if(node && this.isSelected(node)){
50587                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50588                                 Roo.fly(node).removeClass(this.selectedClass);
50589                                 this.selections.remove(node);
50590                                 if(!suppressEvent){
50591                                         this.fireEvent("selectionchange", this, this.selections);
50592                                 }
50593                         }
50594                 }
50595     }
50596 });
50597 /*
50598  * Based on:
50599  * Ext JS Library 1.1.1
50600  * Copyright(c) 2006-2007, Ext JS, LLC.
50601  *
50602  * Originally Released Under LGPL - original licence link has changed is not relivant.
50603  *
50604  * Fork - LGPL
50605  * <script type="text/javascript">
50606  */
50607  
50608 /**
50609  * @class Roo.LayoutManager
50610  * @extends Roo.util.Observable
50611  * Base class for layout managers.
50612  */
50613 Roo.LayoutManager = function(container, config){
50614     Roo.LayoutManager.superclass.constructor.call(this);
50615     this.el = Roo.get(container);
50616     // ie scrollbar fix
50617     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50618         document.body.scroll = "no";
50619     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50620         this.el.position('relative');
50621     }
50622     this.id = this.el.id;
50623     this.el.addClass("x-layout-container");
50624     /** false to disable window resize monitoring @type Boolean */
50625     this.monitorWindowResize = true;
50626     this.regions = {};
50627     this.addEvents({
50628         /**
50629          * @event layout
50630          * Fires when a layout is performed. 
50631          * @param {Roo.LayoutManager} this
50632          */
50633         "layout" : true,
50634         /**
50635          * @event regionresized
50636          * Fires when the user resizes a region. 
50637          * @param {Roo.LayoutRegion} region The resized region
50638          * @param {Number} newSize The new size (width for east/west, height for north/south)
50639          */
50640         "regionresized" : true,
50641         /**
50642          * @event regioncollapsed
50643          * Fires when a region is collapsed. 
50644          * @param {Roo.LayoutRegion} region The collapsed region
50645          */
50646         "regioncollapsed" : true,
50647         /**
50648          * @event regionexpanded
50649          * Fires when a region is expanded.  
50650          * @param {Roo.LayoutRegion} region The expanded region
50651          */
50652         "regionexpanded" : true
50653     });
50654     this.updating = false;
50655     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50656 };
50657
50658 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50659     /**
50660      * Returns true if this layout is currently being updated
50661      * @return {Boolean}
50662      */
50663     isUpdating : function(){
50664         return this.updating; 
50665     },
50666     
50667     /**
50668      * Suspend the LayoutManager from doing auto-layouts while
50669      * making multiple add or remove calls
50670      */
50671     beginUpdate : function(){
50672         this.updating = true;    
50673     },
50674     
50675     /**
50676      * Restore auto-layouts and optionally disable the manager from performing a layout
50677      * @param {Boolean} noLayout true to disable a layout update 
50678      */
50679     endUpdate : function(noLayout){
50680         this.updating = false;
50681         if(!noLayout){
50682             this.layout();
50683         }    
50684     },
50685     
50686     layout: function(){
50687         
50688     },
50689     
50690     onRegionResized : function(region, newSize){
50691         this.fireEvent("regionresized", region, newSize);
50692         this.layout();
50693     },
50694     
50695     onRegionCollapsed : function(region){
50696         this.fireEvent("regioncollapsed", region);
50697     },
50698     
50699     onRegionExpanded : function(region){
50700         this.fireEvent("regionexpanded", region);
50701     },
50702         
50703     /**
50704      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50705      * performs box-model adjustments.
50706      * @return {Object} The size as an object {width: (the width), height: (the height)}
50707      */
50708     getViewSize : function(){
50709         var size;
50710         if(this.el.dom != document.body){
50711             size = this.el.getSize();
50712         }else{
50713             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50714         }
50715         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50716         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50717         return size;
50718     },
50719     
50720     /**
50721      * Returns the Element this layout is bound to.
50722      * @return {Roo.Element}
50723      */
50724     getEl : function(){
50725         return this.el;
50726     },
50727     
50728     /**
50729      * Returns the specified region.
50730      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50731      * @return {Roo.LayoutRegion}
50732      */
50733     getRegion : function(target){
50734         return this.regions[target.toLowerCase()];
50735     },
50736     
50737     onWindowResize : function(){
50738         if(this.monitorWindowResize){
50739             this.layout();
50740         }
50741     }
50742 });/*
50743  * Based on:
50744  * Ext JS Library 1.1.1
50745  * Copyright(c) 2006-2007, Ext JS, LLC.
50746  *
50747  * Originally Released Under LGPL - original licence link has changed is not relivant.
50748  *
50749  * Fork - LGPL
50750  * <script type="text/javascript">
50751  */
50752 /**
50753  * @class Roo.BorderLayout
50754  * @extends Roo.LayoutManager
50755  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50756  * please see: <br><br>
50757  * <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>
50758  * <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>
50759  * Example:
50760  <pre><code>
50761  var layout = new Roo.BorderLayout(document.body, {
50762     north: {
50763         initialSize: 25,
50764         titlebar: false
50765     },
50766     west: {
50767         split:true,
50768         initialSize: 200,
50769         minSize: 175,
50770         maxSize: 400,
50771         titlebar: true,
50772         collapsible: true
50773     },
50774     east: {
50775         split:true,
50776         initialSize: 202,
50777         minSize: 175,
50778         maxSize: 400,
50779         titlebar: true,
50780         collapsible: true
50781     },
50782     south: {
50783         split:true,
50784         initialSize: 100,
50785         minSize: 100,
50786         maxSize: 200,
50787         titlebar: true,
50788         collapsible: true
50789     },
50790     center: {
50791         titlebar: true,
50792         autoScroll:true,
50793         resizeTabs: true,
50794         minTabWidth: 50,
50795         preferredTabWidth: 150
50796     }
50797 });
50798
50799 // shorthand
50800 var CP = Roo.ContentPanel;
50801
50802 layout.beginUpdate();
50803 layout.add("north", new CP("north", "North"));
50804 layout.add("south", new CP("south", {title: "South", closable: true}));
50805 layout.add("west", new CP("west", {title: "West"}));
50806 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50807 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50808 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50809 layout.getRegion("center").showPanel("center1");
50810 layout.endUpdate();
50811 </code></pre>
50812
50813 <b>The container the layout is rendered into can be either the body element or any other element.
50814 If it is not the body element, the container needs to either be an absolute positioned element,
50815 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50816 the container size if it is not the body element.</b>
50817
50818 * @constructor
50819 * Create a new BorderLayout
50820 * @param {String/HTMLElement/Element} container The container this layout is bound to
50821 * @param {Object} config Configuration options
50822  */
50823 Roo.BorderLayout = function(container, config){
50824     config = config || {};
50825     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50826     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50827     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50828         var target = this.factory.validRegions[i];
50829         if(config[target]){
50830             this.addRegion(target, config[target]);
50831         }
50832     }
50833 };
50834
50835 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50836     /**
50837      * Creates and adds a new region if it doesn't already exist.
50838      * @param {String} target The target region key (north, south, east, west or center).
50839      * @param {Object} config The regions config object
50840      * @return {BorderLayoutRegion} The new region
50841      */
50842     addRegion : function(target, config){
50843         if(!this.regions[target]){
50844             var r = this.factory.create(target, this, config);
50845             this.bindRegion(target, r);
50846         }
50847         return this.regions[target];
50848     },
50849
50850     // private (kinda)
50851     bindRegion : function(name, r){
50852         this.regions[name] = r;
50853         r.on("visibilitychange", this.layout, this);
50854         r.on("paneladded", this.layout, this);
50855         r.on("panelremoved", this.layout, this);
50856         r.on("invalidated", this.layout, this);
50857         r.on("resized", this.onRegionResized, this);
50858         r.on("collapsed", this.onRegionCollapsed, this);
50859         r.on("expanded", this.onRegionExpanded, this);
50860     },
50861
50862     /**
50863      * Performs a layout update.
50864      */
50865     layout : function(){
50866         if(this.updating) {
50867             return;
50868         }
50869         var size = this.getViewSize();
50870         var w = size.width;
50871         var h = size.height;
50872         var centerW = w;
50873         var centerH = h;
50874         var centerY = 0;
50875         var centerX = 0;
50876         //var x = 0, y = 0;
50877
50878         var rs = this.regions;
50879         var north = rs["north"];
50880         var south = rs["south"]; 
50881         var west = rs["west"];
50882         var east = rs["east"];
50883         var center = rs["center"];
50884         //if(this.hideOnLayout){ // not supported anymore
50885             //c.el.setStyle("display", "none");
50886         //}
50887         if(north && north.isVisible()){
50888             var b = north.getBox();
50889             var m = north.getMargins();
50890             b.width = w - (m.left+m.right);
50891             b.x = m.left;
50892             b.y = m.top;
50893             centerY = b.height + b.y + m.bottom;
50894             centerH -= centerY;
50895             north.updateBox(this.safeBox(b));
50896         }
50897         if(south && south.isVisible()){
50898             var b = south.getBox();
50899             var m = south.getMargins();
50900             b.width = w - (m.left+m.right);
50901             b.x = m.left;
50902             var totalHeight = (b.height + m.top + m.bottom);
50903             b.y = h - totalHeight + m.top;
50904             centerH -= totalHeight;
50905             south.updateBox(this.safeBox(b));
50906         }
50907         if(west && west.isVisible()){
50908             var b = west.getBox();
50909             var m = west.getMargins();
50910             b.height = centerH - (m.top+m.bottom);
50911             b.x = m.left;
50912             b.y = centerY + m.top;
50913             var totalWidth = (b.width + m.left + m.right);
50914             centerX += totalWidth;
50915             centerW -= totalWidth;
50916             west.updateBox(this.safeBox(b));
50917         }
50918         if(east && east.isVisible()){
50919             var b = east.getBox();
50920             var m = east.getMargins();
50921             b.height = centerH - (m.top+m.bottom);
50922             var totalWidth = (b.width + m.left + m.right);
50923             b.x = w - totalWidth + m.left;
50924             b.y = centerY + m.top;
50925             centerW -= totalWidth;
50926             east.updateBox(this.safeBox(b));
50927         }
50928         if(center){
50929             var m = center.getMargins();
50930             var centerBox = {
50931                 x: centerX + m.left,
50932                 y: centerY + m.top,
50933                 width: centerW - (m.left+m.right),
50934                 height: centerH - (m.top+m.bottom)
50935             };
50936             //if(this.hideOnLayout){
50937                 //center.el.setStyle("display", "block");
50938             //}
50939             center.updateBox(this.safeBox(centerBox));
50940         }
50941         this.el.repaint();
50942         this.fireEvent("layout", this);
50943     },
50944
50945     // private
50946     safeBox : function(box){
50947         box.width = Math.max(0, box.width);
50948         box.height = Math.max(0, box.height);
50949         return box;
50950     },
50951
50952     /**
50953      * Adds a ContentPanel (or subclass) to this layout.
50954      * @param {String} target The target region key (north, south, east, west or center).
50955      * @param {Roo.ContentPanel} panel The panel to add
50956      * @return {Roo.ContentPanel} The added panel
50957      */
50958     add : function(target, panel){
50959          
50960         target = target.toLowerCase();
50961         return this.regions[target].add(panel);
50962     },
50963
50964     /**
50965      * Remove a ContentPanel (or subclass) to this layout.
50966      * @param {String} target The target region key (north, south, east, west or center).
50967      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
50968      * @return {Roo.ContentPanel} The removed panel
50969      */
50970     remove : function(target, panel){
50971         target = target.toLowerCase();
50972         return this.regions[target].remove(panel);
50973     },
50974
50975     /**
50976      * Searches all regions for a panel with the specified id
50977      * @param {String} panelId
50978      * @return {Roo.ContentPanel} The panel or null if it wasn't found
50979      */
50980     findPanel : function(panelId){
50981         var rs = this.regions;
50982         for(var target in rs){
50983             if(typeof rs[target] != "function"){
50984                 var p = rs[target].getPanel(panelId);
50985                 if(p){
50986                     return p;
50987                 }
50988             }
50989         }
50990         return null;
50991     },
50992
50993     /**
50994      * Searches all regions for a panel with the specified id and activates (shows) it.
50995      * @param {String/ContentPanel} panelId The panels id or the panel itself
50996      * @return {Roo.ContentPanel} The shown panel or null
50997      */
50998     showPanel : function(panelId) {
50999       var rs = this.regions;
51000       for(var target in rs){
51001          var r = rs[target];
51002          if(typeof r != "function"){
51003             if(r.hasPanel(panelId)){
51004                return r.showPanel(panelId);
51005             }
51006          }
51007       }
51008       return null;
51009    },
51010
51011    /**
51012      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51013      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51014      */
51015     restoreState : function(provider){
51016         if(!provider){
51017             provider = Roo.state.Manager;
51018         }
51019         var sm = new Roo.LayoutStateManager();
51020         sm.init(this, provider);
51021     },
51022
51023     /**
51024      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51025      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51026      * a valid ContentPanel config object.  Example:
51027      * <pre><code>
51028 // Create the main layout
51029 var layout = new Roo.BorderLayout('main-ct', {
51030     west: {
51031         split:true,
51032         minSize: 175,
51033         titlebar: true
51034     },
51035     center: {
51036         title:'Components'
51037     }
51038 }, 'main-ct');
51039
51040 // Create and add multiple ContentPanels at once via configs
51041 layout.batchAdd({
51042    west: {
51043        id: 'source-files',
51044        autoCreate:true,
51045        title:'Ext Source Files',
51046        autoScroll:true,
51047        fitToFrame:true
51048    },
51049    center : {
51050        el: cview,
51051        autoScroll:true,
51052        fitToFrame:true,
51053        toolbar: tb,
51054        resizeEl:'cbody'
51055    }
51056 });
51057 </code></pre>
51058      * @param {Object} regions An object containing ContentPanel configs by region name
51059      */
51060     batchAdd : function(regions){
51061         this.beginUpdate();
51062         for(var rname in regions){
51063             var lr = this.regions[rname];
51064             if(lr){
51065                 this.addTypedPanels(lr, regions[rname]);
51066             }
51067         }
51068         this.endUpdate();
51069     },
51070
51071     // private
51072     addTypedPanels : function(lr, ps){
51073         if(typeof ps == 'string'){
51074             lr.add(new Roo.ContentPanel(ps));
51075         }
51076         else if(ps instanceof Array){
51077             for(var i =0, len = ps.length; i < len; i++){
51078                 this.addTypedPanels(lr, ps[i]);
51079             }
51080         }
51081         else if(!ps.events){ // raw config?
51082             var el = ps.el;
51083             delete ps.el; // prevent conflict
51084             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51085         }
51086         else {  // panel object assumed!
51087             lr.add(ps);
51088         }
51089     },
51090     /**
51091      * Adds a xtype elements to the layout.
51092      * <pre><code>
51093
51094 layout.addxtype({
51095        xtype : 'ContentPanel',
51096        region: 'west',
51097        items: [ .... ]
51098    }
51099 );
51100
51101 layout.addxtype({
51102         xtype : 'NestedLayoutPanel',
51103         region: 'west',
51104         layout: {
51105            center: { },
51106            west: { }   
51107         },
51108         items : [ ... list of content panels or nested layout panels.. ]
51109    }
51110 );
51111 </code></pre>
51112      * @param {Object} cfg Xtype definition of item to add.
51113      */
51114     addxtype : function(cfg)
51115     {
51116         // basically accepts a pannel...
51117         // can accept a layout region..!?!?
51118         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51119         
51120         if (!cfg.xtype.match(/Panel$/)) {
51121             return false;
51122         }
51123         var ret = false;
51124         
51125         if (typeof(cfg.region) == 'undefined') {
51126             Roo.log("Failed to add Panel, region was not set");
51127             Roo.log(cfg);
51128             return false;
51129         }
51130         var region = cfg.region;
51131         delete cfg.region;
51132         
51133           
51134         var xitems = [];
51135         if (cfg.items) {
51136             xitems = cfg.items;
51137             delete cfg.items;
51138         }
51139         var nb = false;
51140         
51141         switch(cfg.xtype) 
51142         {
51143             case 'ContentPanel':  // ContentPanel (el, cfg)
51144             case 'ScrollPanel':  // ContentPanel (el, cfg)
51145             case 'ViewPanel': 
51146                 if(cfg.autoCreate) {
51147                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51148                 } else {
51149                     var el = this.el.createChild();
51150                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51151                 }
51152                 
51153                 this.add(region, ret);
51154                 break;
51155             
51156             
51157             case 'TreePanel': // our new panel!
51158                 cfg.el = this.el.createChild();
51159                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51160                 this.add(region, ret);
51161                 break;
51162             
51163             case 'NestedLayoutPanel': 
51164                 // create a new Layout (which is  a Border Layout...
51165                 var el = this.el.createChild();
51166                 var clayout = cfg.layout;
51167                 delete cfg.layout;
51168                 clayout.items   = clayout.items  || [];
51169                 // replace this exitems with the clayout ones..
51170                 xitems = clayout.items;
51171                  
51172                 
51173                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51174                     cfg.background = false;
51175                 }
51176                 var layout = new Roo.BorderLayout(el, clayout);
51177                 
51178                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51179                 //console.log('adding nested layout panel '  + cfg.toSource());
51180                 this.add(region, ret);
51181                 nb = {}; /// find first...
51182                 break;
51183                 
51184             case 'GridPanel': 
51185             
51186                 // needs grid and region
51187                 
51188                 //var el = this.getRegion(region).el.createChild();
51189                 var el = this.el.createChild();
51190                 // create the grid first...
51191                 
51192                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51193                 delete cfg.grid;
51194                 if (region == 'center' && this.active ) {
51195                     cfg.background = false;
51196                 }
51197                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51198                 
51199                 this.add(region, ret);
51200                 if (cfg.background) {
51201                     ret.on('activate', function(gp) {
51202                         if (!gp.grid.rendered) {
51203                             gp.grid.render();
51204                         }
51205                     });
51206                 } else {
51207                     grid.render();
51208                 }
51209                 break;
51210            
51211            
51212            
51213                 
51214                 
51215                 
51216             default:
51217                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51218                     
51219                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51220                     this.add(region, ret);
51221                 } else {
51222                 
51223                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51224                     return null;
51225                 }
51226                 
51227              // GridPanel (grid, cfg)
51228             
51229         }
51230         this.beginUpdate();
51231         // add children..
51232         var region = '';
51233         var abn = {};
51234         Roo.each(xitems, function(i)  {
51235             region = nb && i.region ? i.region : false;
51236             
51237             var add = ret.addxtype(i);
51238            
51239             if (region) {
51240                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51241                 if (!i.background) {
51242                     abn[region] = nb[region] ;
51243                 }
51244             }
51245             
51246         });
51247         this.endUpdate();
51248
51249         // make the last non-background panel active..
51250         //if (nb) { Roo.log(abn); }
51251         if (nb) {
51252             
51253             for(var r in abn) {
51254                 region = this.getRegion(r);
51255                 if (region) {
51256                     // tried using nb[r], but it does not work..
51257                      
51258                     region.showPanel(abn[r]);
51259                    
51260                 }
51261             }
51262         }
51263         return ret;
51264         
51265     }
51266 });
51267
51268 /**
51269  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51270  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51271  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51272  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51273  * <pre><code>
51274 // shorthand
51275 var CP = Roo.ContentPanel;
51276
51277 var layout = Roo.BorderLayout.create({
51278     north: {
51279         initialSize: 25,
51280         titlebar: false,
51281         panels: [new CP("north", "North")]
51282     },
51283     west: {
51284         split:true,
51285         initialSize: 200,
51286         minSize: 175,
51287         maxSize: 400,
51288         titlebar: true,
51289         collapsible: true,
51290         panels: [new CP("west", {title: "West"})]
51291     },
51292     east: {
51293         split:true,
51294         initialSize: 202,
51295         minSize: 175,
51296         maxSize: 400,
51297         titlebar: true,
51298         collapsible: true,
51299         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51300     },
51301     south: {
51302         split:true,
51303         initialSize: 100,
51304         minSize: 100,
51305         maxSize: 200,
51306         titlebar: true,
51307         collapsible: true,
51308         panels: [new CP("south", {title: "South", closable: true})]
51309     },
51310     center: {
51311         titlebar: true,
51312         autoScroll:true,
51313         resizeTabs: true,
51314         minTabWidth: 50,
51315         preferredTabWidth: 150,
51316         panels: [
51317             new CP("center1", {title: "Close Me", closable: true}),
51318             new CP("center2", {title: "Center Panel", closable: false})
51319         ]
51320     }
51321 }, document.body);
51322
51323 layout.getRegion("center").showPanel("center1");
51324 </code></pre>
51325  * @param config
51326  * @param targetEl
51327  */
51328 Roo.BorderLayout.create = function(config, targetEl){
51329     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51330     layout.beginUpdate();
51331     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51332     for(var j = 0, jlen = regions.length; j < jlen; j++){
51333         var lr = regions[j];
51334         if(layout.regions[lr] && config[lr].panels){
51335             var r = layout.regions[lr];
51336             var ps = config[lr].panels;
51337             layout.addTypedPanels(r, ps);
51338         }
51339     }
51340     layout.endUpdate();
51341     return layout;
51342 };
51343
51344 // private
51345 Roo.BorderLayout.RegionFactory = {
51346     // private
51347     validRegions : ["north","south","east","west","center"],
51348
51349     // private
51350     create : function(target, mgr, config){
51351         target = target.toLowerCase();
51352         if(config.lightweight || config.basic){
51353             return new Roo.BasicLayoutRegion(mgr, config, target);
51354         }
51355         switch(target){
51356             case "north":
51357                 return new Roo.NorthLayoutRegion(mgr, config);
51358             case "south":
51359                 return new Roo.SouthLayoutRegion(mgr, config);
51360             case "east":
51361                 return new Roo.EastLayoutRegion(mgr, config);
51362             case "west":
51363                 return new Roo.WestLayoutRegion(mgr, config);
51364             case "center":
51365                 return new Roo.CenterLayoutRegion(mgr, config);
51366         }
51367         throw 'Layout region "'+target+'" not supported.';
51368     }
51369 };/*
51370  * Based on:
51371  * Ext JS Library 1.1.1
51372  * Copyright(c) 2006-2007, Ext JS, LLC.
51373  *
51374  * Originally Released Under LGPL - original licence link has changed is not relivant.
51375  *
51376  * Fork - LGPL
51377  * <script type="text/javascript">
51378  */
51379  
51380 /**
51381  * @class Roo.BasicLayoutRegion
51382  * @extends Roo.util.Observable
51383  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51384  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51385  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51386  */
51387 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51388     this.mgr = mgr;
51389     this.position  = pos;
51390     this.events = {
51391         /**
51392          * @scope Roo.BasicLayoutRegion
51393          */
51394         
51395         /**
51396          * @event beforeremove
51397          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51398          * @param {Roo.LayoutRegion} this
51399          * @param {Roo.ContentPanel} panel The panel
51400          * @param {Object} e The cancel event object
51401          */
51402         "beforeremove" : true,
51403         /**
51404          * @event invalidated
51405          * Fires when the layout for this region is changed.
51406          * @param {Roo.LayoutRegion} this
51407          */
51408         "invalidated" : true,
51409         /**
51410          * @event visibilitychange
51411          * Fires when this region is shown or hidden 
51412          * @param {Roo.LayoutRegion} this
51413          * @param {Boolean} visibility true or false
51414          */
51415         "visibilitychange" : true,
51416         /**
51417          * @event paneladded
51418          * Fires when a panel is added. 
51419          * @param {Roo.LayoutRegion} this
51420          * @param {Roo.ContentPanel} panel The panel
51421          */
51422         "paneladded" : true,
51423         /**
51424          * @event panelremoved
51425          * Fires when a panel is removed. 
51426          * @param {Roo.LayoutRegion} this
51427          * @param {Roo.ContentPanel} panel The panel
51428          */
51429         "panelremoved" : true,
51430         /**
51431          * @event beforecollapse
51432          * Fires when this region before collapse.
51433          * @param {Roo.LayoutRegion} this
51434          */
51435         "beforecollapse" : true,
51436         /**
51437          * @event collapsed
51438          * Fires when this region is collapsed.
51439          * @param {Roo.LayoutRegion} this
51440          */
51441         "collapsed" : true,
51442         /**
51443          * @event expanded
51444          * Fires when this region is expanded.
51445          * @param {Roo.LayoutRegion} this
51446          */
51447         "expanded" : true,
51448         /**
51449          * @event slideshow
51450          * Fires when this region is slid into view.
51451          * @param {Roo.LayoutRegion} this
51452          */
51453         "slideshow" : true,
51454         /**
51455          * @event slidehide
51456          * Fires when this region slides out of view. 
51457          * @param {Roo.LayoutRegion} this
51458          */
51459         "slidehide" : true,
51460         /**
51461          * @event panelactivated
51462          * Fires when a panel is activated. 
51463          * @param {Roo.LayoutRegion} this
51464          * @param {Roo.ContentPanel} panel The activated panel
51465          */
51466         "panelactivated" : true,
51467         /**
51468          * @event resized
51469          * Fires when the user resizes this region. 
51470          * @param {Roo.LayoutRegion} this
51471          * @param {Number} newSize The new size (width for east/west, height for north/south)
51472          */
51473         "resized" : true
51474     };
51475     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51476     this.panels = new Roo.util.MixedCollection();
51477     this.panels.getKey = this.getPanelId.createDelegate(this);
51478     this.box = null;
51479     this.activePanel = null;
51480     // ensure listeners are added...
51481     
51482     if (config.listeners || config.events) {
51483         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51484             listeners : config.listeners || {},
51485             events : config.events || {}
51486         });
51487     }
51488     
51489     if(skipConfig !== true){
51490         this.applyConfig(config);
51491     }
51492 };
51493
51494 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51495     getPanelId : function(p){
51496         return p.getId();
51497     },
51498     
51499     applyConfig : function(config){
51500         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51501         this.config = config;
51502         
51503     },
51504     
51505     /**
51506      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51507      * the width, for horizontal (north, south) the height.
51508      * @param {Number} newSize The new width or height
51509      */
51510     resizeTo : function(newSize){
51511         var el = this.el ? this.el :
51512                  (this.activePanel ? this.activePanel.getEl() : null);
51513         if(el){
51514             switch(this.position){
51515                 case "east":
51516                 case "west":
51517                     el.setWidth(newSize);
51518                     this.fireEvent("resized", this, newSize);
51519                 break;
51520                 case "north":
51521                 case "south":
51522                     el.setHeight(newSize);
51523                     this.fireEvent("resized", this, newSize);
51524                 break;                
51525             }
51526         }
51527     },
51528     
51529     getBox : function(){
51530         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51531     },
51532     
51533     getMargins : function(){
51534         return this.margins;
51535     },
51536     
51537     updateBox : function(box){
51538         this.box = box;
51539         var el = this.activePanel.getEl();
51540         el.dom.style.left = box.x + "px";
51541         el.dom.style.top = box.y + "px";
51542         this.activePanel.setSize(box.width, box.height);
51543     },
51544     
51545     /**
51546      * Returns the container element for this region.
51547      * @return {Roo.Element}
51548      */
51549     getEl : function(){
51550         return this.activePanel;
51551     },
51552     
51553     /**
51554      * Returns true if this region is currently visible.
51555      * @return {Boolean}
51556      */
51557     isVisible : function(){
51558         return this.activePanel ? true : false;
51559     },
51560     
51561     setActivePanel : function(panel){
51562         panel = this.getPanel(panel);
51563         if(this.activePanel && this.activePanel != panel){
51564             this.activePanel.setActiveState(false);
51565             this.activePanel.getEl().setLeftTop(-10000,-10000);
51566         }
51567         this.activePanel = panel;
51568         panel.setActiveState(true);
51569         if(this.box){
51570             panel.setSize(this.box.width, this.box.height);
51571         }
51572         this.fireEvent("panelactivated", this, panel);
51573         this.fireEvent("invalidated");
51574     },
51575     
51576     /**
51577      * Show the specified panel.
51578      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51579      * @return {Roo.ContentPanel} The shown panel or null
51580      */
51581     showPanel : function(panel){
51582         if(panel = this.getPanel(panel)){
51583             this.setActivePanel(panel);
51584         }
51585         return panel;
51586     },
51587     
51588     /**
51589      * Get the active panel for this region.
51590      * @return {Roo.ContentPanel} The active panel or null
51591      */
51592     getActivePanel : function(){
51593         return this.activePanel;
51594     },
51595     
51596     /**
51597      * Add the passed ContentPanel(s)
51598      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51599      * @return {Roo.ContentPanel} The panel added (if only one was added)
51600      */
51601     add : function(panel){
51602         if(arguments.length > 1){
51603             for(var i = 0, len = arguments.length; i < len; i++) {
51604                 this.add(arguments[i]);
51605             }
51606             return null;
51607         }
51608         if(this.hasPanel(panel)){
51609             this.showPanel(panel);
51610             return panel;
51611         }
51612         var el = panel.getEl();
51613         if(el.dom.parentNode != this.mgr.el.dom){
51614             this.mgr.el.dom.appendChild(el.dom);
51615         }
51616         if(panel.setRegion){
51617             panel.setRegion(this);
51618         }
51619         this.panels.add(panel);
51620         el.setStyle("position", "absolute");
51621         if(!panel.background){
51622             this.setActivePanel(panel);
51623             if(this.config.initialSize && this.panels.getCount()==1){
51624                 this.resizeTo(this.config.initialSize);
51625             }
51626         }
51627         this.fireEvent("paneladded", this, panel);
51628         return panel;
51629     },
51630     
51631     /**
51632      * Returns true if the panel is in this region.
51633      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51634      * @return {Boolean}
51635      */
51636     hasPanel : function(panel){
51637         if(typeof panel == "object"){ // must be panel obj
51638             panel = panel.getId();
51639         }
51640         return this.getPanel(panel) ? true : false;
51641     },
51642     
51643     /**
51644      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51645      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51646      * @param {Boolean} preservePanel Overrides the config preservePanel option
51647      * @return {Roo.ContentPanel} The panel that was removed
51648      */
51649     remove : function(panel, preservePanel){
51650         panel = this.getPanel(panel);
51651         if(!panel){
51652             return null;
51653         }
51654         var e = {};
51655         this.fireEvent("beforeremove", this, panel, e);
51656         if(e.cancel === true){
51657             return null;
51658         }
51659         var panelId = panel.getId();
51660         this.panels.removeKey(panelId);
51661         return panel;
51662     },
51663     
51664     /**
51665      * Returns the panel specified or null if it's not in this region.
51666      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51667      * @return {Roo.ContentPanel}
51668      */
51669     getPanel : function(id){
51670         if(typeof id == "object"){ // must be panel obj
51671             return id;
51672         }
51673         return this.panels.get(id);
51674     },
51675     
51676     /**
51677      * Returns this regions position (north/south/east/west/center).
51678      * @return {String} 
51679      */
51680     getPosition: function(){
51681         return this.position;    
51682     }
51683 });/*
51684  * Based on:
51685  * Ext JS Library 1.1.1
51686  * Copyright(c) 2006-2007, Ext JS, LLC.
51687  *
51688  * Originally Released Under LGPL - original licence link has changed is not relivant.
51689  *
51690  * Fork - LGPL
51691  * <script type="text/javascript">
51692  */
51693  
51694 /**
51695  * @class Roo.LayoutRegion
51696  * @extends Roo.BasicLayoutRegion
51697  * This class represents a region in a layout manager.
51698  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51699  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51700  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51701  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51702  * @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})
51703  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51704  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51705  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51706  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51707  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51708  * @cfg {String}    title           The title for the region (overrides panel titles)
51709  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51710  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51711  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51712  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51713  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51714  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51715  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51716  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51717  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51718  * @cfg {Boolean}   showPin         True to show a pin button
51719  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51720  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51721  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51722  * @cfg {Number}    width           For East/West panels
51723  * @cfg {Number}    height          For North/South panels
51724  * @cfg {Boolean}   split           To show the splitter
51725  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51726  */
51727 Roo.LayoutRegion = function(mgr, config, pos){
51728     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51729     var dh = Roo.DomHelper;
51730     /** This region's container element 
51731     * @type Roo.Element */
51732     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51733     /** This region's title element 
51734     * @type Roo.Element */
51735
51736     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51737         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51738         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51739     ]}, true);
51740     this.titleEl.enableDisplayMode();
51741     /** This region's title text element 
51742     * @type HTMLElement */
51743     this.titleTextEl = this.titleEl.dom.firstChild;
51744     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51745     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51746     this.closeBtn.enableDisplayMode();
51747     this.closeBtn.on("click", this.closeClicked, this);
51748     this.closeBtn.hide();
51749
51750     this.createBody(config);
51751     this.visible = true;
51752     this.collapsed = false;
51753
51754     if(config.hideWhenEmpty){
51755         this.hide();
51756         this.on("paneladded", this.validateVisibility, this);
51757         this.on("panelremoved", this.validateVisibility, this);
51758     }
51759     this.applyConfig(config);
51760 };
51761
51762 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51763
51764     createBody : function(){
51765         /** This region's body element 
51766         * @type Roo.Element */
51767         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51768     },
51769
51770     applyConfig : function(c){
51771         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51772             var dh = Roo.DomHelper;
51773             if(c.titlebar !== false){
51774                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51775                 this.collapseBtn.on("click", this.collapse, this);
51776                 this.collapseBtn.enableDisplayMode();
51777
51778                 if(c.showPin === true || this.showPin){
51779                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51780                     this.stickBtn.enableDisplayMode();
51781                     this.stickBtn.on("click", this.expand, this);
51782                     this.stickBtn.hide();
51783                 }
51784             }
51785             /** This region's collapsed element
51786             * @type Roo.Element */
51787             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51788                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51789             ]}, true);
51790             if(c.floatable !== false){
51791                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51792                this.collapsedEl.on("click", this.collapseClick, this);
51793             }
51794
51795             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51796                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51797                    id: "message", unselectable: "on", style:{"float":"left"}});
51798                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51799              }
51800             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51801             this.expandBtn.on("click", this.expand, this);
51802         }
51803         if(this.collapseBtn){
51804             this.collapseBtn.setVisible(c.collapsible == true);
51805         }
51806         this.cmargins = c.cmargins || this.cmargins ||
51807                          (this.position == "west" || this.position == "east" ?
51808                              {top: 0, left: 2, right:2, bottom: 0} :
51809                              {top: 2, left: 0, right:0, bottom: 2});
51810         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51811         this.bottomTabs = c.tabPosition != "top";
51812         this.autoScroll = c.autoScroll || false;
51813         if(this.autoScroll){
51814             this.bodyEl.setStyle("overflow", "auto");
51815         }else{
51816             this.bodyEl.setStyle("overflow", "hidden");
51817         }
51818         //if(c.titlebar !== false){
51819             if((!c.titlebar && !c.title) || c.titlebar === false){
51820                 this.titleEl.hide();
51821             }else{
51822                 this.titleEl.show();
51823                 if(c.title){
51824                     this.titleTextEl.innerHTML = c.title;
51825                 }
51826             }
51827         //}
51828         this.duration = c.duration || .30;
51829         this.slideDuration = c.slideDuration || .45;
51830         this.config = c;
51831         if(c.collapsed){
51832             this.collapse(true);
51833         }
51834         if(c.hidden){
51835             this.hide();
51836         }
51837     },
51838     /**
51839      * Returns true if this region is currently visible.
51840      * @return {Boolean}
51841      */
51842     isVisible : function(){
51843         return this.visible;
51844     },
51845
51846     /**
51847      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51848      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51849      */
51850     setCollapsedTitle : function(title){
51851         title = title || "&#160;";
51852         if(this.collapsedTitleTextEl){
51853             this.collapsedTitleTextEl.innerHTML = title;
51854         }
51855     },
51856
51857     getBox : function(){
51858         var b;
51859         if(!this.collapsed){
51860             b = this.el.getBox(false, true);
51861         }else{
51862             b = this.collapsedEl.getBox(false, true);
51863         }
51864         return b;
51865     },
51866
51867     getMargins : function(){
51868         return this.collapsed ? this.cmargins : this.margins;
51869     },
51870
51871     highlight : function(){
51872         this.el.addClass("x-layout-panel-dragover");
51873     },
51874
51875     unhighlight : function(){
51876         this.el.removeClass("x-layout-panel-dragover");
51877     },
51878
51879     updateBox : function(box){
51880         this.box = box;
51881         if(!this.collapsed){
51882             this.el.dom.style.left = box.x + "px";
51883             this.el.dom.style.top = box.y + "px";
51884             this.updateBody(box.width, box.height);
51885         }else{
51886             this.collapsedEl.dom.style.left = box.x + "px";
51887             this.collapsedEl.dom.style.top = box.y + "px";
51888             this.collapsedEl.setSize(box.width, box.height);
51889         }
51890         if(this.tabs){
51891             this.tabs.autoSizeTabs();
51892         }
51893     },
51894
51895     updateBody : function(w, h){
51896         if(w !== null){
51897             this.el.setWidth(w);
51898             w -= this.el.getBorderWidth("rl");
51899             if(this.config.adjustments){
51900                 w += this.config.adjustments[0];
51901             }
51902         }
51903         if(h !== null){
51904             this.el.setHeight(h);
51905             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
51906             h -= this.el.getBorderWidth("tb");
51907             if(this.config.adjustments){
51908                 h += this.config.adjustments[1];
51909             }
51910             this.bodyEl.setHeight(h);
51911             if(this.tabs){
51912                 h = this.tabs.syncHeight(h);
51913             }
51914         }
51915         if(this.panelSize){
51916             w = w !== null ? w : this.panelSize.width;
51917             h = h !== null ? h : this.panelSize.height;
51918         }
51919         if(this.activePanel){
51920             var el = this.activePanel.getEl();
51921             w = w !== null ? w : el.getWidth();
51922             h = h !== null ? h : el.getHeight();
51923             this.panelSize = {width: w, height: h};
51924             this.activePanel.setSize(w, h);
51925         }
51926         if(Roo.isIE && this.tabs){
51927             this.tabs.el.repaint();
51928         }
51929     },
51930
51931     /**
51932      * Returns the container element for this region.
51933      * @return {Roo.Element}
51934      */
51935     getEl : function(){
51936         return this.el;
51937     },
51938
51939     /**
51940      * Hides this region.
51941      */
51942     hide : function(){
51943         if(!this.collapsed){
51944             this.el.dom.style.left = "-2000px";
51945             this.el.hide();
51946         }else{
51947             this.collapsedEl.dom.style.left = "-2000px";
51948             this.collapsedEl.hide();
51949         }
51950         this.visible = false;
51951         this.fireEvent("visibilitychange", this, false);
51952     },
51953
51954     /**
51955      * Shows this region if it was previously hidden.
51956      */
51957     show : function(){
51958         if(!this.collapsed){
51959             this.el.show();
51960         }else{
51961             this.collapsedEl.show();
51962         }
51963         this.visible = true;
51964         this.fireEvent("visibilitychange", this, true);
51965     },
51966
51967     closeClicked : function(){
51968         if(this.activePanel){
51969             this.remove(this.activePanel);
51970         }
51971     },
51972
51973     collapseClick : function(e){
51974         if(this.isSlid){
51975            e.stopPropagation();
51976            this.slideIn();
51977         }else{
51978            e.stopPropagation();
51979            this.slideOut();
51980         }
51981     },
51982
51983     /**
51984      * Collapses this region.
51985      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
51986      */
51987     collapse : function(skipAnim, skipCheck = false){
51988         if(this.collapsed) {
51989             return;
51990         }
51991         
51992         if(this.fireEvent("beforecollapse", this) != false || skipCheck){
51993             
51994             this.collapsed = true;
51995             if(this.split){
51996                 this.split.el.hide();
51997             }
51998             if(this.config.animate && skipAnim !== true){
51999                 this.fireEvent("invalidated", this);
52000                 this.animateCollapse();
52001             }else{
52002                 this.el.setLocation(-20000,-20000);
52003                 this.el.hide();
52004                 this.collapsedEl.show();
52005                 this.fireEvent("collapsed", this);
52006                 this.fireEvent("invalidated", this);
52007             }
52008         }
52009         
52010     },
52011
52012     animateCollapse : function(){
52013         // overridden
52014     },
52015
52016     /**
52017      * Expands this region if it was previously collapsed.
52018      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52019      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52020      */
52021     expand : function(e, skipAnim){
52022         if(e) {
52023             e.stopPropagation();
52024         }
52025         if(!this.collapsed || this.el.hasActiveFx()) {
52026             return;
52027         }
52028         if(this.isSlid){
52029             this.afterSlideIn();
52030             skipAnim = true;
52031         }
52032         this.collapsed = false;
52033         if(this.config.animate && skipAnim !== true){
52034             this.animateExpand();
52035         }else{
52036             this.el.show();
52037             if(this.split){
52038                 this.split.el.show();
52039             }
52040             this.collapsedEl.setLocation(-2000,-2000);
52041             this.collapsedEl.hide();
52042             this.fireEvent("invalidated", this);
52043             this.fireEvent("expanded", this);
52044         }
52045     },
52046
52047     animateExpand : function(){
52048         // overridden
52049     },
52050
52051     initTabs : function()
52052     {
52053         this.bodyEl.setStyle("overflow", "hidden");
52054         var ts = new Roo.TabPanel(
52055                 this.bodyEl.dom,
52056                 {
52057                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52058                     disableTooltips: this.config.disableTabTips,
52059                     toolbar : this.config.toolbar
52060                 }
52061         );
52062         if(this.config.hideTabs){
52063             ts.stripWrap.setDisplayed(false);
52064         }
52065         this.tabs = ts;
52066         ts.resizeTabs = this.config.resizeTabs === true;
52067         ts.minTabWidth = this.config.minTabWidth || 40;
52068         ts.maxTabWidth = this.config.maxTabWidth || 250;
52069         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52070         ts.monitorResize = false;
52071         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52072         ts.bodyEl.addClass('x-layout-tabs-body');
52073         this.panels.each(this.initPanelAsTab, this);
52074     },
52075
52076     initPanelAsTab : function(panel){
52077         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52078                     this.config.closeOnTab && panel.isClosable());
52079         if(panel.tabTip !== undefined){
52080             ti.setTooltip(panel.tabTip);
52081         }
52082         ti.on("activate", function(){
52083               this.setActivePanel(panel);
52084         }, this);
52085         if(this.config.closeOnTab){
52086             ti.on("beforeclose", function(t, e){
52087                 e.cancel = true;
52088                 this.remove(panel);
52089             }, this);
52090         }
52091         return ti;
52092     },
52093
52094     updatePanelTitle : function(panel, title){
52095         if(this.activePanel == panel){
52096             this.updateTitle(title);
52097         }
52098         if(this.tabs){
52099             var ti = this.tabs.getTab(panel.getEl().id);
52100             ti.setText(title);
52101             if(panel.tabTip !== undefined){
52102                 ti.setTooltip(panel.tabTip);
52103             }
52104         }
52105     },
52106
52107     updateTitle : function(title){
52108         if(this.titleTextEl && !this.config.title){
52109             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52110         }
52111     },
52112
52113     setActivePanel : function(panel){
52114         panel = this.getPanel(panel);
52115         if(this.activePanel && this.activePanel != panel){
52116             this.activePanel.setActiveState(false);
52117         }
52118         this.activePanel = panel;
52119         panel.setActiveState(true);
52120         if(this.panelSize){
52121             panel.setSize(this.panelSize.width, this.panelSize.height);
52122         }
52123         if(this.closeBtn){
52124             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52125         }
52126         this.updateTitle(panel.getTitle());
52127         if(this.tabs){
52128             this.fireEvent("invalidated", this);
52129         }
52130         this.fireEvent("panelactivated", this, panel);
52131     },
52132
52133     /**
52134      * Shows the specified panel.
52135      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52136      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52137      */
52138     showPanel : function(panel)
52139     {
52140         panel = this.getPanel(panel);
52141         if(panel){
52142             if(this.tabs){
52143                 var tab = this.tabs.getTab(panel.getEl().id);
52144                 if(tab.isHidden()){
52145                     this.tabs.unhideTab(tab.id);
52146                 }
52147                 tab.activate();
52148             }else{
52149                 this.setActivePanel(panel);
52150             }
52151         }
52152         return panel;
52153     },
52154
52155     /**
52156      * Get the active panel for this region.
52157      * @return {Roo.ContentPanel} The active panel or null
52158      */
52159     getActivePanel : function(){
52160         return this.activePanel;
52161     },
52162
52163     validateVisibility : function(){
52164         if(this.panels.getCount() < 1){
52165             this.updateTitle("&#160;");
52166             this.closeBtn.hide();
52167             this.hide();
52168         }else{
52169             if(!this.isVisible()){
52170                 this.show();
52171             }
52172         }
52173     },
52174
52175     /**
52176      * Adds the passed ContentPanel(s) to this region.
52177      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52178      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52179      */
52180     add : function(panel){
52181         if(arguments.length > 1){
52182             for(var i = 0, len = arguments.length; i < len; i++) {
52183                 this.add(arguments[i]);
52184             }
52185             return null;
52186         }
52187         if(this.hasPanel(panel)){
52188             this.showPanel(panel);
52189             return panel;
52190         }
52191         panel.setRegion(this);
52192         this.panels.add(panel);
52193         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52194             this.bodyEl.dom.appendChild(panel.getEl().dom);
52195             if(panel.background !== true){
52196                 this.setActivePanel(panel);
52197             }
52198             this.fireEvent("paneladded", this, panel);
52199             return panel;
52200         }
52201         if(!this.tabs){
52202             this.initTabs();
52203         }else{
52204             this.initPanelAsTab(panel);
52205         }
52206         if(panel.background !== true){
52207             this.tabs.activate(panel.getEl().id);
52208         }
52209         this.fireEvent("paneladded", this, panel);
52210         return panel;
52211     },
52212
52213     /**
52214      * Hides the tab for the specified panel.
52215      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52216      */
52217     hidePanel : function(panel){
52218         if(this.tabs && (panel = this.getPanel(panel))){
52219             this.tabs.hideTab(panel.getEl().id);
52220         }
52221     },
52222
52223     /**
52224      * Unhides the tab for a previously hidden panel.
52225      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52226      */
52227     unhidePanel : function(panel){
52228         if(this.tabs && (panel = this.getPanel(panel))){
52229             this.tabs.unhideTab(panel.getEl().id);
52230         }
52231     },
52232
52233     clearPanels : function(){
52234         while(this.panels.getCount() > 0){
52235              this.remove(this.panels.first());
52236         }
52237     },
52238
52239     /**
52240      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52241      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52242      * @param {Boolean} preservePanel Overrides the config preservePanel option
52243      * @return {Roo.ContentPanel} The panel that was removed
52244      */
52245     remove : function(panel, preservePanel){
52246         panel = this.getPanel(panel);
52247         if(!panel){
52248             return null;
52249         }
52250         var e = {};
52251         this.fireEvent("beforeremove", this, panel, e);
52252         if(e.cancel === true){
52253             return null;
52254         }
52255         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52256         var panelId = panel.getId();
52257         this.panels.removeKey(panelId);
52258         if(preservePanel){
52259             document.body.appendChild(panel.getEl().dom);
52260         }
52261         if(this.tabs){
52262             this.tabs.removeTab(panel.getEl().id);
52263         }else if (!preservePanel){
52264             this.bodyEl.dom.removeChild(panel.getEl().dom);
52265         }
52266         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52267             var p = this.panels.first();
52268             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52269             tempEl.appendChild(p.getEl().dom);
52270             this.bodyEl.update("");
52271             this.bodyEl.dom.appendChild(p.getEl().dom);
52272             tempEl = null;
52273             this.updateTitle(p.getTitle());
52274             this.tabs = null;
52275             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52276             this.setActivePanel(p);
52277         }
52278         panel.setRegion(null);
52279         if(this.activePanel == panel){
52280             this.activePanel = null;
52281         }
52282         if(this.config.autoDestroy !== false && preservePanel !== true){
52283             try{panel.destroy();}catch(e){}
52284         }
52285         this.fireEvent("panelremoved", this, panel);
52286         return panel;
52287     },
52288
52289     /**
52290      * Returns the TabPanel component used by this region
52291      * @return {Roo.TabPanel}
52292      */
52293     getTabs : function(){
52294         return this.tabs;
52295     },
52296
52297     createTool : function(parentEl, className){
52298         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52299             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52300         btn.addClassOnOver("x-layout-tools-button-over");
52301         return btn;
52302     }
52303 });/*
52304  * Based on:
52305  * Ext JS Library 1.1.1
52306  * Copyright(c) 2006-2007, Ext JS, LLC.
52307  *
52308  * Originally Released Under LGPL - original licence link has changed is not relivant.
52309  *
52310  * Fork - LGPL
52311  * <script type="text/javascript">
52312  */
52313  
52314
52315
52316 /**
52317  * @class Roo.SplitLayoutRegion
52318  * @extends Roo.LayoutRegion
52319  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52320  */
52321 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52322     this.cursor = cursor;
52323     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52324 };
52325
52326 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52327     splitTip : "Drag to resize.",
52328     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52329     useSplitTips : false,
52330
52331     applyConfig : function(config){
52332         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52333         if(config.split){
52334             if(!this.split){
52335                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52336                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52337                 /** The SplitBar for this region 
52338                 * @type Roo.SplitBar */
52339                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52340                 this.split.on("moved", this.onSplitMove, this);
52341                 this.split.useShim = config.useShim === true;
52342                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52343                 if(this.useSplitTips){
52344                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52345                 }
52346                 if(config.collapsible){
52347                     this.split.el.on("dblclick", this.collapse,  this);
52348                 }
52349             }
52350             if(typeof config.minSize != "undefined"){
52351                 this.split.minSize = config.minSize;
52352             }
52353             if(typeof config.maxSize != "undefined"){
52354                 this.split.maxSize = config.maxSize;
52355             }
52356             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52357                 this.hideSplitter();
52358             }
52359         }
52360     },
52361
52362     getHMaxSize : function(){
52363          var cmax = this.config.maxSize || 10000;
52364          var center = this.mgr.getRegion("center");
52365          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52366     },
52367
52368     getVMaxSize : function(){
52369          var cmax = this.config.maxSize || 10000;
52370          var center = this.mgr.getRegion("center");
52371          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52372     },
52373
52374     onSplitMove : function(split, newSize){
52375         this.fireEvent("resized", this, newSize);
52376     },
52377     
52378     /** 
52379      * Returns the {@link Roo.SplitBar} for this region.
52380      * @return {Roo.SplitBar}
52381      */
52382     getSplitBar : function(){
52383         return this.split;
52384     },
52385     
52386     hide : function(){
52387         this.hideSplitter();
52388         Roo.SplitLayoutRegion.superclass.hide.call(this);
52389     },
52390
52391     hideSplitter : function(){
52392         if(this.split){
52393             this.split.el.setLocation(-2000,-2000);
52394             this.split.el.hide();
52395         }
52396     },
52397
52398     show : function(){
52399         if(this.split){
52400             this.split.el.show();
52401         }
52402         Roo.SplitLayoutRegion.superclass.show.call(this);
52403     },
52404     
52405     beforeSlide: function(){
52406         if(Roo.isGecko){// firefox overflow auto bug workaround
52407             this.bodyEl.clip();
52408             if(this.tabs) {
52409                 this.tabs.bodyEl.clip();
52410             }
52411             if(this.activePanel){
52412                 this.activePanel.getEl().clip();
52413                 
52414                 if(this.activePanel.beforeSlide){
52415                     this.activePanel.beforeSlide();
52416                 }
52417             }
52418         }
52419     },
52420     
52421     afterSlide : function(){
52422         if(Roo.isGecko){// firefox overflow auto bug workaround
52423             this.bodyEl.unclip();
52424             if(this.tabs) {
52425                 this.tabs.bodyEl.unclip();
52426             }
52427             if(this.activePanel){
52428                 this.activePanel.getEl().unclip();
52429                 if(this.activePanel.afterSlide){
52430                     this.activePanel.afterSlide();
52431                 }
52432             }
52433         }
52434     },
52435
52436     initAutoHide : function(){
52437         if(this.autoHide !== false){
52438             if(!this.autoHideHd){
52439                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52440                 this.autoHideHd = {
52441                     "mouseout": function(e){
52442                         if(!e.within(this.el, true)){
52443                             st.delay(500);
52444                         }
52445                     },
52446                     "mouseover" : function(e){
52447                         st.cancel();
52448                     },
52449                     scope : this
52450                 };
52451             }
52452             this.el.on(this.autoHideHd);
52453         }
52454     },
52455
52456     clearAutoHide : function(){
52457         if(this.autoHide !== false){
52458             this.el.un("mouseout", this.autoHideHd.mouseout);
52459             this.el.un("mouseover", this.autoHideHd.mouseover);
52460         }
52461     },
52462
52463     clearMonitor : function(){
52464         Roo.get(document).un("click", this.slideInIf, this);
52465     },
52466
52467     // these names are backwards but not changed for compat
52468     slideOut : function(){
52469         if(this.isSlid || this.el.hasActiveFx()){
52470             return;
52471         }
52472         this.isSlid = true;
52473         if(this.collapseBtn){
52474             this.collapseBtn.hide();
52475         }
52476         this.closeBtnState = this.closeBtn.getStyle('display');
52477         this.closeBtn.hide();
52478         if(this.stickBtn){
52479             this.stickBtn.show();
52480         }
52481         this.el.show();
52482         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52483         this.beforeSlide();
52484         this.el.setStyle("z-index", 10001);
52485         this.el.slideIn(this.getSlideAnchor(), {
52486             callback: function(){
52487                 this.afterSlide();
52488                 this.initAutoHide();
52489                 Roo.get(document).on("click", this.slideInIf, this);
52490                 this.fireEvent("slideshow", this);
52491             },
52492             scope: this,
52493             block: true
52494         });
52495     },
52496
52497     afterSlideIn : function(){
52498         this.clearAutoHide();
52499         this.isSlid = false;
52500         this.clearMonitor();
52501         this.el.setStyle("z-index", "");
52502         if(this.collapseBtn){
52503             this.collapseBtn.show();
52504         }
52505         this.closeBtn.setStyle('display', this.closeBtnState);
52506         if(this.stickBtn){
52507             this.stickBtn.hide();
52508         }
52509         this.fireEvent("slidehide", this);
52510     },
52511
52512     slideIn : function(cb){
52513         if(!this.isSlid || this.el.hasActiveFx()){
52514             Roo.callback(cb);
52515             return;
52516         }
52517         this.isSlid = false;
52518         this.beforeSlide();
52519         this.el.slideOut(this.getSlideAnchor(), {
52520             callback: function(){
52521                 this.el.setLeftTop(-10000, -10000);
52522                 this.afterSlide();
52523                 this.afterSlideIn();
52524                 Roo.callback(cb);
52525             },
52526             scope: this,
52527             block: true
52528         });
52529     },
52530     
52531     slideInIf : function(e){
52532         if(!e.within(this.el)){
52533             this.slideIn();
52534         }
52535     },
52536
52537     animateCollapse : function(){
52538         this.beforeSlide();
52539         this.el.setStyle("z-index", 20000);
52540         var anchor = this.getSlideAnchor();
52541         this.el.slideOut(anchor, {
52542             callback : function(){
52543                 this.el.setStyle("z-index", "");
52544                 this.collapsedEl.slideIn(anchor, {duration:.3});
52545                 this.afterSlide();
52546                 this.el.setLocation(-10000,-10000);
52547                 this.el.hide();
52548                 this.fireEvent("collapsed", this);
52549             },
52550             scope: this,
52551             block: true
52552         });
52553     },
52554
52555     animateExpand : function(){
52556         this.beforeSlide();
52557         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52558         this.el.setStyle("z-index", 20000);
52559         this.collapsedEl.hide({
52560             duration:.1
52561         });
52562         this.el.slideIn(this.getSlideAnchor(), {
52563             callback : function(){
52564                 this.el.setStyle("z-index", "");
52565                 this.afterSlide();
52566                 if(this.split){
52567                     this.split.el.show();
52568                 }
52569                 this.fireEvent("invalidated", this);
52570                 this.fireEvent("expanded", this);
52571             },
52572             scope: this,
52573             block: true
52574         });
52575     },
52576
52577     anchors : {
52578         "west" : "left",
52579         "east" : "right",
52580         "north" : "top",
52581         "south" : "bottom"
52582     },
52583
52584     sanchors : {
52585         "west" : "l",
52586         "east" : "r",
52587         "north" : "t",
52588         "south" : "b"
52589     },
52590
52591     canchors : {
52592         "west" : "tl-tr",
52593         "east" : "tr-tl",
52594         "north" : "tl-bl",
52595         "south" : "bl-tl"
52596     },
52597
52598     getAnchor : function(){
52599         return this.anchors[this.position];
52600     },
52601
52602     getCollapseAnchor : function(){
52603         return this.canchors[this.position];
52604     },
52605
52606     getSlideAnchor : function(){
52607         return this.sanchors[this.position];
52608     },
52609
52610     getAlignAdj : function(){
52611         var cm = this.cmargins;
52612         switch(this.position){
52613             case "west":
52614                 return [0, 0];
52615             break;
52616             case "east":
52617                 return [0, 0];
52618             break;
52619             case "north":
52620                 return [0, 0];
52621             break;
52622             case "south":
52623                 return [0, 0];
52624             break;
52625         }
52626     },
52627
52628     getExpandAdj : function(){
52629         var c = this.collapsedEl, cm = this.cmargins;
52630         switch(this.position){
52631             case "west":
52632                 return [-(cm.right+c.getWidth()+cm.left), 0];
52633             break;
52634             case "east":
52635                 return [cm.right+c.getWidth()+cm.left, 0];
52636             break;
52637             case "north":
52638                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52639             break;
52640             case "south":
52641                 return [0, cm.top+cm.bottom+c.getHeight()];
52642             break;
52643         }
52644     }
52645 });/*
52646  * Based on:
52647  * Ext JS Library 1.1.1
52648  * Copyright(c) 2006-2007, Ext JS, LLC.
52649  *
52650  * Originally Released Under LGPL - original licence link has changed is not relivant.
52651  *
52652  * Fork - LGPL
52653  * <script type="text/javascript">
52654  */
52655 /*
52656  * These classes are private internal classes
52657  */
52658 Roo.CenterLayoutRegion = function(mgr, config){
52659     Roo.LayoutRegion.call(this, mgr, config, "center");
52660     this.visible = true;
52661     this.minWidth = config.minWidth || 20;
52662     this.minHeight = config.minHeight || 20;
52663 };
52664
52665 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52666     hide : function(){
52667         // center panel can't be hidden
52668     },
52669     
52670     show : function(){
52671         // center panel can't be hidden
52672     },
52673     
52674     getMinWidth: function(){
52675         return this.minWidth;
52676     },
52677     
52678     getMinHeight: function(){
52679         return this.minHeight;
52680     }
52681 });
52682
52683
52684 Roo.NorthLayoutRegion = function(mgr, config){
52685     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52686     if(this.split){
52687         this.split.placement = Roo.SplitBar.TOP;
52688         this.split.orientation = Roo.SplitBar.VERTICAL;
52689         this.split.el.addClass("x-layout-split-v");
52690     }
52691     var size = config.initialSize || config.height;
52692     if(typeof size != "undefined"){
52693         this.el.setHeight(size);
52694     }
52695 };
52696 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52697     orientation: Roo.SplitBar.VERTICAL,
52698     getBox : function(){
52699         if(this.collapsed){
52700             return this.collapsedEl.getBox();
52701         }
52702         var box = this.el.getBox();
52703         if(this.split){
52704             box.height += this.split.el.getHeight();
52705         }
52706         return box;
52707     },
52708     
52709     updateBox : function(box){
52710         if(this.split && !this.collapsed){
52711             box.height -= this.split.el.getHeight();
52712             this.split.el.setLeft(box.x);
52713             this.split.el.setTop(box.y+box.height);
52714             this.split.el.setWidth(box.width);
52715         }
52716         if(this.collapsed){
52717             this.updateBody(box.width, null);
52718         }
52719         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52720     }
52721 });
52722
52723 Roo.SouthLayoutRegion = function(mgr, config){
52724     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52725     if(this.split){
52726         this.split.placement = Roo.SplitBar.BOTTOM;
52727         this.split.orientation = Roo.SplitBar.VERTICAL;
52728         this.split.el.addClass("x-layout-split-v");
52729     }
52730     var size = config.initialSize || config.height;
52731     if(typeof size != "undefined"){
52732         this.el.setHeight(size);
52733     }
52734 };
52735 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52736     orientation: Roo.SplitBar.VERTICAL,
52737     getBox : function(){
52738         if(this.collapsed){
52739             return this.collapsedEl.getBox();
52740         }
52741         var box = this.el.getBox();
52742         if(this.split){
52743             var sh = this.split.el.getHeight();
52744             box.height += sh;
52745             box.y -= sh;
52746         }
52747         return box;
52748     },
52749     
52750     updateBox : function(box){
52751         if(this.split && !this.collapsed){
52752             var sh = this.split.el.getHeight();
52753             box.height -= sh;
52754             box.y += sh;
52755             this.split.el.setLeft(box.x);
52756             this.split.el.setTop(box.y-sh);
52757             this.split.el.setWidth(box.width);
52758         }
52759         if(this.collapsed){
52760             this.updateBody(box.width, null);
52761         }
52762         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52763     }
52764 });
52765
52766 Roo.EastLayoutRegion = function(mgr, config){
52767     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52768     if(this.split){
52769         this.split.placement = Roo.SplitBar.RIGHT;
52770         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52771         this.split.el.addClass("x-layout-split-h");
52772     }
52773     var size = config.initialSize || config.width;
52774     if(typeof size != "undefined"){
52775         this.el.setWidth(size);
52776     }
52777 };
52778 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52779     orientation: Roo.SplitBar.HORIZONTAL,
52780     getBox : function(){
52781         if(this.collapsed){
52782             return this.collapsedEl.getBox();
52783         }
52784         var box = this.el.getBox();
52785         if(this.split){
52786             var sw = this.split.el.getWidth();
52787             box.width += sw;
52788             box.x -= sw;
52789         }
52790         return box;
52791     },
52792
52793     updateBox : function(box){
52794         if(this.split && !this.collapsed){
52795             var sw = this.split.el.getWidth();
52796             box.width -= sw;
52797             this.split.el.setLeft(box.x);
52798             this.split.el.setTop(box.y);
52799             this.split.el.setHeight(box.height);
52800             box.x += sw;
52801         }
52802         if(this.collapsed){
52803             this.updateBody(null, box.height);
52804         }
52805         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52806     }
52807 });
52808
52809 Roo.WestLayoutRegion = function(mgr, config){
52810     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52811     if(this.split){
52812         this.split.placement = Roo.SplitBar.LEFT;
52813         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52814         this.split.el.addClass("x-layout-split-h");
52815     }
52816     var size = config.initialSize || config.width;
52817     if(typeof size != "undefined"){
52818         this.el.setWidth(size);
52819     }
52820 };
52821 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52822     orientation: Roo.SplitBar.HORIZONTAL,
52823     getBox : function(){
52824         if(this.collapsed){
52825             return this.collapsedEl.getBox();
52826         }
52827         var box = this.el.getBox();
52828         if(this.split){
52829             box.width += this.split.el.getWidth();
52830         }
52831         return box;
52832     },
52833     
52834     updateBox : function(box){
52835         if(this.split && !this.collapsed){
52836             var sw = this.split.el.getWidth();
52837             box.width -= sw;
52838             this.split.el.setLeft(box.x+box.width);
52839             this.split.el.setTop(box.y);
52840             this.split.el.setHeight(box.height);
52841         }
52842         if(this.collapsed){
52843             this.updateBody(null, box.height);
52844         }
52845         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52846     }
52847 });
52848 /*
52849  * Based on:
52850  * Ext JS Library 1.1.1
52851  * Copyright(c) 2006-2007, Ext JS, LLC.
52852  *
52853  * Originally Released Under LGPL - original licence link has changed is not relivant.
52854  *
52855  * Fork - LGPL
52856  * <script type="text/javascript">
52857  */
52858  
52859  
52860 /*
52861  * Private internal class for reading and applying state
52862  */
52863 Roo.LayoutStateManager = function(layout){
52864      // default empty state
52865      this.state = {
52866         north: {},
52867         south: {},
52868         east: {},
52869         west: {}       
52870     };
52871 };
52872
52873 Roo.LayoutStateManager.prototype = {
52874     init : function(layout, provider){
52875         this.provider = provider;
52876         var state = provider.get(layout.id+"-layout-state");
52877         if(state){
52878             var wasUpdating = layout.isUpdating();
52879             if(!wasUpdating){
52880                 layout.beginUpdate();
52881             }
52882             for(var key in state){
52883                 if(typeof state[key] != "function"){
52884                     var rstate = state[key];
52885                     var r = layout.getRegion(key);
52886                     if(r && rstate){
52887                         if(rstate.size){
52888                             r.resizeTo(rstate.size);
52889                         }
52890                         if(rstate.collapsed == true){
52891                             r.collapse(true);
52892                         }else{
52893                             r.expand(null, true);
52894                         }
52895                     }
52896                 }
52897             }
52898             if(!wasUpdating){
52899                 layout.endUpdate();
52900             }
52901             this.state = state; 
52902         }
52903         this.layout = layout;
52904         layout.on("regionresized", this.onRegionResized, this);
52905         layout.on("regioncollapsed", this.onRegionCollapsed, this);
52906         layout.on("regionexpanded", this.onRegionExpanded, this);
52907     },
52908     
52909     storeState : function(){
52910         this.provider.set(this.layout.id+"-layout-state", this.state);
52911     },
52912     
52913     onRegionResized : function(region, newSize){
52914         this.state[region.getPosition()].size = newSize;
52915         this.storeState();
52916     },
52917     
52918     onRegionCollapsed : function(region){
52919         this.state[region.getPosition()].collapsed = true;
52920         this.storeState();
52921     },
52922     
52923     onRegionExpanded : function(region){
52924         this.state[region.getPosition()].collapsed = false;
52925         this.storeState();
52926     }
52927 };/*
52928  * Based on:
52929  * Ext JS Library 1.1.1
52930  * Copyright(c) 2006-2007, Ext JS, LLC.
52931  *
52932  * Originally Released Under LGPL - original licence link has changed is not relivant.
52933  *
52934  * Fork - LGPL
52935  * <script type="text/javascript">
52936  */
52937 /**
52938  * @class Roo.ContentPanel
52939  * @extends Roo.util.Observable
52940  * A basic ContentPanel element.
52941  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
52942  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
52943  * @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
52944  * @cfg {Boolean}   closable      True if the panel can be closed/removed
52945  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
52946  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
52947  * @cfg {Toolbar}   toolbar       A toolbar for this panel
52948  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
52949  * @cfg {String} title          The title for this panel
52950  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
52951  * @cfg {String} url            Calls {@link #setUrl} with this value
52952  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
52953  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
52954  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
52955  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
52956
52957  * @constructor
52958  * Create a new ContentPanel.
52959  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
52960  * @param {String/Object} config A string to set only the title or a config object
52961  * @param {String} content (optional) Set the HTML content for this panel
52962  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
52963  */
52964 Roo.ContentPanel = function(el, config, content){
52965     
52966      
52967     /*
52968     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
52969         config = el;
52970         el = Roo.id();
52971     }
52972     if (config && config.parentLayout) { 
52973         el = config.parentLayout.el.createChild(); 
52974     }
52975     */
52976     if(el.autoCreate){ // xtype is available if this is called from factory
52977         config = el;
52978         el = Roo.id();
52979     }
52980     this.el = Roo.get(el);
52981     if(!this.el && config && config.autoCreate){
52982         if(typeof config.autoCreate == "object"){
52983             if(!config.autoCreate.id){
52984                 config.autoCreate.id = config.id||el;
52985             }
52986             this.el = Roo.DomHelper.append(document.body,
52987                         config.autoCreate, true);
52988         }else{
52989             this.el = Roo.DomHelper.append(document.body,
52990                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
52991         }
52992     }
52993     this.closable = false;
52994     this.loaded = false;
52995     this.active = false;
52996     if(typeof config == "string"){
52997         this.title = config;
52998     }else{
52999         Roo.apply(this, config);
53000     }
53001     
53002     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53003         this.wrapEl = this.el.wrap();
53004         this.toolbar.container = this.el.insertSibling(false, 'before');
53005         this.toolbar = new Roo.Toolbar(this.toolbar);
53006     }
53007     
53008     // xtype created footer. - not sure if will work as we normally have to render first..
53009     if (this.footer && !this.footer.el && this.footer.xtype) {
53010         if (!this.wrapEl) {
53011             this.wrapEl = this.el.wrap();
53012         }
53013     
53014         this.footer.container = this.wrapEl.createChild();
53015          
53016         this.footer = Roo.factory(this.footer, Roo);
53017         
53018     }
53019     
53020     if(this.resizeEl){
53021         this.resizeEl = Roo.get(this.resizeEl, true);
53022     }else{
53023         this.resizeEl = this.el;
53024     }
53025     // handle view.xtype
53026     
53027  
53028     
53029     
53030     this.addEvents({
53031         /**
53032          * @event activate
53033          * Fires when this panel is activated. 
53034          * @param {Roo.ContentPanel} this
53035          */
53036         "activate" : true,
53037         /**
53038          * @event deactivate
53039          * Fires when this panel is activated. 
53040          * @param {Roo.ContentPanel} this
53041          */
53042         "deactivate" : true,
53043
53044         /**
53045          * @event resize
53046          * Fires when this panel is resized if fitToFrame is true.
53047          * @param {Roo.ContentPanel} this
53048          * @param {Number} width The width after any component adjustments
53049          * @param {Number} height The height after any component adjustments
53050          */
53051         "resize" : true,
53052         
53053          /**
53054          * @event render
53055          * Fires when this tab is created
53056          * @param {Roo.ContentPanel} this
53057          */
53058         "render" : true
53059         
53060         
53061         
53062     });
53063     
53064
53065     
53066     
53067     if(this.autoScroll){
53068         this.resizeEl.setStyle("overflow", "auto");
53069     } else {
53070         // fix randome scrolling
53071         this.el.on('scroll', function() {
53072             Roo.log('fix random scolling');
53073             this.scrollTo('top',0); 
53074         });
53075     }
53076     content = content || this.content;
53077     if(content){
53078         this.setContent(content);
53079     }
53080     if(config && config.url){
53081         this.setUrl(this.url, this.params, this.loadOnce);
53082     }
53083     
53084     
53085     
53086     Roo.ContentPanel.superclass.constructor.call(this);
53087     
53088     if (this.view && typeof(this.view.xtype) != 'undefined') {
53089         this.view.el = this.el.appendChild(document.createElement("div"));
53090         this.view = Roo.factory(this.view); 
53091         this.view.render  &&  this.view.render(false, '');  
53092     }
53093     
53094     
53095     this.fireEvent('render', this);
53096 };
53097
53098 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53099     tabTip:'',
53100     setRegion : function(region){
53101         this.region = region;
53102         if(region){
53103            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53104         }else{
53105            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53106         } 
53107     },
53108     
53109     /**
53110      * Returns the toolbar for this Panel if one was configured. 
53111      * @return {Roo.Toolbar} 
53112      */
53113     getToolbar : function(){
53114         return this.toolbar;
53115     },
53116     
53117     setActiveState : function(active){
53118         this.active = active;
53119         if(!active){
53120             this.fireEvent("deactivate", this);
53121         }else{
53122             this.fireEvent("activate", this);
53123         }
53124     },
53125     /**
53126      * Updates this panel's element
53127      * @param {String} content The new content
53128      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53129     */
53130     setContent : function(content, loadScripts){
53131         this.el.update(content, loadScripts);
53132     },
53133
53134     ignoreResize : function(w, h){
53135         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53136             return true;
53137         }else{
53138             this.lastSize = {width: w, height: h};
53139             return false;
53140         }
53141     },
53142     /**
53143      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53144      * @return {Roo.UpdateManager} The UpdateManager
53145      */
53146     getUpdateManager : function(){
53147         return this.el.getUpdateManager();
53148     },
53149      /**
53150      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53151      * @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:
53152 <pre><code>
53153 panel.load({
53154     url: "your-url.php",
53155     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53156     callback: yourFunction,
53157     scope: yourObject, //(optional scope)
53158     discardUrl: false,
53159     nocache: false,
53160     text: "Loading...",
53161     timeout: 30,
53162     scripts: false
53163 });
53164 </code></pre>
53165      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53166      * 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.
53167      * @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}
53168      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53169      * @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.
53170      * @return {Roo.ContentPanel} this
53171      */
53172     load : function(){
53173         var um = this.el.getUpdateManager();
53174         um.update.apply(um, arguments);
53175         return this;
53176     },
53177
53178
53179     /**
53180      * 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.
53181      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53182      * @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)
53183      * @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)
53184      * @return {Roo.UpdateManager} The UpdateManager
53185      */
53186     setUrl : function(url, params, loadOnce){
53187         if(this.refreshDelegate){
53188             this.removeListener("activate", this.refreshDelegate);
53189         }
53190         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53191         this.on("activate", this.refreshDelegate);
53192         return this.el.getUpdateManager();
53193     },
53194     
53195     _handleRefresh : function(url, params, loadOnce){
53196         if(!loadOnce || !this.loaded){
53197             var updater = this.el.getUpdateManager();
53198             updater.update(url, params, this._setLoaded.createDelegate(this));
53199         }
53200     },
53201     
53202     _setLoaded : function(){
53203         this.loaded = true;
53204     }, 
53205     
53206     /**
53207      * Returns this panel's id
53208      * @return {String} 
53209      */
53210     getId : function(){
53211         return this.el.id;
53212     },
53213     
53214     /** 
53215      * Returns this panel's element - used by regiosn to add.
53216      * @return {Roo.Element} 
53217      */
53218     getEl : function(){
53219         return this.wrapEl || this.el;
53220     },
53221     
53222     adjustForComponents : function(width, height)
53223     {
53224         //Roo.log('adjustForComponents ');
53225         if(this.resizeEl != this.el){
53226             width -= this.el.getFrameWidth('lr');
53227             height -= this.el.getFrameWidth('tb');
53228         }
53229         if(this.toolbar){
53230             var te = this.toolbar.getEl();
53231             height -= te.getHeight();
53232             te.setWidth(width);
53233         }
53234         if(this.footer){
53235             var te = this.footer.getEl();
53236             Roo.log("footer:" + te.getHeight());
53237             
53238             height -= te.getHeight();
53239             te.setWidth(width);
53240         }
53241         
53242         
53243         if(this.adjustments){
53244             width += this.adjustments[0];
53245             height += this.adjustments[1];
53246         }
53247         return {"width": width, "height": height};
53248     },
53249     
53250     setSize : function(width, height){
53251         if(this.fitToFrame && !this.ignoreResize(width, height)){
53252             if(this.fitContainer && this.resizeEl != this.el){
53253                 this.el.setSize(width, height);
53254             }
53255             var size = this.adjustForComponents(width, height);
53256             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53257             this.fireEvent('resize', this, size.width, size.height);
53258         }
53259     },
53260     
53261     /**
53262      * Returns this panel's title
53263      * @return {String} 
53264      */
53265     getTitle : function(){
53266         return this.title;
53267     },
53268     
53269     /**
53270      * Set this panel's title
53271      * @param {String} title
53272      */
53273     setTitle : function(title){
53274         this.title = title;
53275         if(this.region){
53276             this.region.updatePanelTitle(this, title);
53277         }
53278     },
53279     
53280     /**
53281      * Returns true is this panel was configured to be closable
53282      * @return {Boolean} 
53283      */
53284     isClosable : function(){
53285         return this.closable;
53286     },
53287     
53288     beforeSlide : function(){
53289         this.el.clip();
53290         this.resizeEl.clip();
53291     },
53292     
53293     afterSlide : function(){
53294         this.el.unclip();
53295         this.resizeEl.unclip();
53296     },
53297     
53298     /**
53299      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53300      *   Will fail silently if the {@link #setUrl} method has not been called.
53301      *   This does not activate the panel, just updates its content.
53302      */
53303     refresh : function(){
53304         if(this.refreshDelegate){
53305            this.loaded = false;
53306            this.refreshDelegate();
53307         }
53308     },
53309     
53310     /**
53311      * Destroys this panel
53312      */
53313     destroy : function(){
53314         this.el.removeAllListeners();
53315         var tempEl = document.createElement("span");
53316         tempEl.appendChild(this.el.dom);
53317         tempEl.innerHTML = "";
53318         this.el.remove();
53319         this.el = null;
53320     },
53321     
53322     /**
53323      * form - if the content panel contains a form - this is a reference to it.
53324      * @type {Roo.form.Form}
53325      */
53326     form : false,
53327     /**
53328      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53329      *    This contains a reference to it.
53330      * @type {Roo.View}
53331      */
53332     view : false,
53333     
53334       /**
53335      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53336      * <pre><code>
53337
53338 layout.addxtype({
53339        xtype : 'Form',
53340        items: [ .... ]
53341    }
53342 );
53343
53344 </code></pre>
53345      * @param {Object} cfg Xtype definition of item to add.
53346      */
53347     
53348     addxtype : function(cfg) {
53349         // add form..
53350         if (cfg.xtype.match(/^Form$/)) {
53351             
53352             var el;
53353             //if (this.footer) {
53354             //    el = this.footer.container.insertSibling(false, 'before');
53355             //} else {
53356                 el = this.el.createChild();
53357             //}
53358
53359             this.form = new  Roo.form.Form(cfg);
53360             
53361             
53362             if ( this.form.allItems.length) {
53363                 this.form.render(el.dom);
53364             }
53365             return this.form;
53366         }
53367         // should only have one of theses..
53368         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53369             // views.. should not be just added - used named prop 'view''
53370             
53371             cfg.el = this.el.appendChild(document.createElement("div"));
53372             // factory?
53373             
53374             var ret = new Roo.factory(cfg);
53375              
53376              ret.render && ret.render(false, ''); // render blank..
53377             this.view = ret;
53378             return ret;
53379         }
53380         return false;
53381     }
53382 });
53383
53384 /**
53385  * @class Roo.GridPanel
53386  * @extends Roo.ContentPanel
53387  * @constructor
53388  * Create a new GridPanel.
53389  * @param {Roo.grid.Grid} grid The grid for this panel
53390  * @param {String/Object} config A string to set only the panel's title, or a config object
53391  */
53392 Roo.GridPanel = function(grid, config){
53393     
53394   
53395     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53396         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53397         
53398     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53399     
53400     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53401     
53402     if(this.toolbar){
53403         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53404     }
53405     // xtype created footer. - not sure if will work as we normally have to render first..
53406     if (this.footer && !this.footer.el && this.footer.xtype) {
53407         
53408         this.footer.container = this.grid.getView().getFooterPanel(true);
53409         this.footer.dataSource = this.grid.dataSource;
53410         this.footer = Roo.factory(this.footer, Roo);
53411         
53412     }
53413     
53414     grid.monitorWindowResize = false; // turn off autosizing
53415     grid.autoHeight = false;
53416     grid.autoWidth = false;
53417     this.grid = grid;
53418     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53419 };
53420
53421 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53422     getId : function(){
53423         return this.grid.id;
53424     },
53425     
53426     /**
53427      * Returns the grid for this panel
53428      * @return {Roo.grid.Grid} 
53429      */
53430     getGrid : function(){
53431         return this.grid;    
53432     },
53433     
53434     setSize : function(width, height){
53435         if(!this.ignoreResize(width, height)){
53436             var grid = this.grid;
53437             var size = this.adjustForComponents(width, height);
53438             grid.getGridEl().setSize(size.width, size.height);
53439             grid.autoSize();
53440         }
53441     },
53442     
53443     beforeSlide : function(){
53444         this.grid.getView().scroller.clip();
53445     },
53446     
53447     afterSlide : function(){
53448         this.grid.getView().scroller.unclip();
53449     },
53450     
53451     destroy : function(){
53452         this.grid.destroy();
53453         delete this.grid;
53454         Roo.GridPanel.superclass.destroy.call(this); 
53455     }
53456 });
53457
53458
53459 /**
53460  * @class Roo.NestedLayoutPanel
53461  * @extends Roo.ContentPanel
53462  * @constructor
53463  * Create a new NestedLayoutPanel.
53464  * 
53465  * 
53466  * @param {Roo.BorderLayout} layout The layout for this panel
53467  * @param {String/Object} config A string to set only the title or a config object
53468  */
53469 Roo.NestedLayoutPanel = function(layout, config)
53470 {
53471     // construct with only one argument..
53472     /* FIXME - implement nicer consturctors
53473     if (layout.layout) {
53474         config = layout;
53475         layout = config.layout;
53476         delete config.layout;
53477     }
53478     if (layout.xtype && !layout.getEl) {
53479         // then layout needs constructing..
53480         layout = Roo.factory(layout, Roo);
53481     }
53482     */
53483     
53484     
53485     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53486     
53487     layout.monitorWindowResize = false; // turn off autosizing
53488     this.layout = layout;
53489     this.layout.getEl().addClass("x-layout-nested-layout");
53490     
53491     
53492     
53493     
53494 };
53495
53496 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53497
53498     setSize : function(width, height){
53499         if(!this.ignoreResize(width, height)){
53500             var size = this.adjustForComponents(width, height);
53501             var el = this.layout.getEl();
53502             el.setSize(size.width, size.height);
53503             var touch = el.dom.offsetWidth;
53504             this.layout.layout();
53505             // ie requires a double layout on the first pass
53506             if(Roo.isIE && !this.initialized){
53507                 this.initialized = true;
53508                 this.layout.layout();
53509             }
53510         }
53511     },
53512     
53513     // activate all subpanels if not currently active..
53514     
53515     setActiveState : function(active){
53516         this.active = active;
53517         if(!active){
53518             this.fireEvent("deactivate", this);
53519             return;
53520         }
53521         
53522         this.fireEvent("activate", this);
53523         // not sure if this should happen before or after..
53524         if (!this.layout) {
53525             return; // should not happen..
53526         }
53527         var reg = false;
53528         for (var r in this.layout.regions) {
53529             reg = this.layout.getRegion(r);
53530             if (reg.getActivePanel()) {
53531                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53532                 reg.setActivePanel(reg.getActivePanel());
53533                 continue;
53534             }
53535             if (!reg.panels.length) {
53536                 continue;
53537             }
53538             reg.showPanel(reg.getPanel(0));
53539         }
53540         
53541         
53542         
53543         
53544     },
53545     
53546     /**
53547      * Returns the nested BorderLayout for this panel
53548      * @return {Roo.BorderLayout} 
53549      */
53550     getLayout : function(){
53551         return this.layout;
53552     },
53553     
53554      /**
53555      * Adds a xtype elements to the layout of the nested panel
53556      * <pre><code>
53557
53558 panel.addxtype({
53559        xtype : 'ContentPanel',
53560        region: 'west',
53561        items: [ .... ]
53562    }
53563 );
53564
53565 panel.addxtype({
53566         xtype : 'NestedLayoutPanel',
53567         region: 'west',
53568         layout: {
53569            center: { },
53570            west: { }   
53571         },
53572         items : [ ... list of content panels or nested layout panels.. ]
53573    }
53574 );
53575 </code></pre>
53576      * @param {Object} cfg Xtype definition of item to add.
53577      */
53578     addxtype : function(cfg) {
53579         return this.layout.addxtype(cfg);
53580     
53581     }
53582 });
53583
53584 Roo.ScrollPanel = function(el, config, content){
53585     config = config || {};
53586     config.fitToFrame = true;
53587     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53588     
53589     this.el.dom.style.overflow = "hidden";
53590     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53591     this.el.removeClass("x-layout-inactive-content");
53592     this.el.on("mousewheel", this.onWheel, this);
53593
53594     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53595     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53596     up.unselectable(); down.unselectable();
53597     up.on("click", this.scrollUp, this);
53598     down.on("click", this.scrollDown, this);
53599     up.addClassOnOver("x-scroller-btn-over");
53600     down.addClassOnOver("x-scroller-btn-over");
53601     up.addClassOnClick("x-scroller-btn-click");
53602     down.addClassOnClick("x-scroller-btn-click");
53603     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53604
53605     this.resizeEl = this.el;
53606     this.el = wrap; this.up = up; this.down = down;
53607 };
53608
53609 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53610     increment : 100,
53611     wheelIncrement : 5,
53612     scrollUp : function(){
53613         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53614     },
53615
53616     scrollDown : function(){
53617         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53618     },
53619
53620     afterScroll : function(){
53621         var el = this.resizeEl;
53622         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53623         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53624         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53625     },
53626
53627     setSize : function(){
53628         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53629         this.afterScroll();
53630     },
53631
53632     onWheel : function(e){
53633         var d = e.getWheelDelta();
53634         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53635         this.afterScroll();
53636         e.stopEvent();
53637     },
53638
53639     setContent : function(content, loadScripts){
53640         this.resizeEl.update(content, loadScripts);
53641     }
53642
53643 });
53644
53645
53646
53647
53648
53649
53650
53651
53652
53653 /**
53654  * @class Roo.TreePanel
53655  * @extends Roo.ContentPanel
53656  * @constructor
53657  * Create a new TreePanel. - defaults to fit/scoll contents.
53658  * @param {String/Object} config A string to set only the panel's title, or a config object
53659  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53660  */
53661 Roo.TreePanel = function(config){
53662     var el = config.el;
53663     var tree = config.tree;
53664     delete config.tree; 
53665     delete config.el; // hopefull!
53666     
53667     // wrapper for IE7 strict & safari scroll issue
53668     
53669     var treeEl = el.createChild();
53670     config.resizeEl = treeEl;
53671     
53672     
53673     
53674     Roo.TreePanel.superclass.constructor.call(this, el, config);
53675  
53676  
53677     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53678     //console.log(tree);
53679     this.on('activate', function()
53680     {
53681         if (this.tree.rendered) {
53682             return;
53683         }
53684         //console.log('render tree');
53685         this.tree.render();
53686     });
53687     // this should not be needed.. - it's actually the 'el' that resizes?
53688     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53689     
53690     //this.on('resize',  function (cp, w, h) {
53691     //        this.tree.innerCt.setWidth(w);
53692     //        this.tree.innerCt.setHeight(h);
53693     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53694     //});
53695
53696         
53697     
53698 };
53699
53700 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53701     fitToFrame : true,
53702     autoScroll : true
53703 });
53704
53705
53706
53707
53708
53709
53710
53711
53712
53713
53714
53715 /*
53716  * Based on:
53717  * Ext JS Library 1.1.1
53718  * Copyright(c) 2006-2007, Ext JS, LLC.
53719  *
53720  * Originally Released Under LGPL - original licence link has changed is not relivant.
53721  *
53722  * Fork - LGPL
53723  * <script type="text/javascript">
53724  */
53725  
53726
53727 /**
53728  * @class Roo.ReaderLayout
53729  * @extends Roo.BorderLayout
53730  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53731  * center region containing two nested regions (a top one for a list view and one for item preview below),
53732  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53733  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53734  * expedites the setup of the overall layout and regions for this common application style.
53735  * Example:
53736  <pre><code>
53737 var reader = new Roo.ReaderLayout();
53738 var CP = Roo.ContentPanel;  // shortcut for adding
53739
53740 reader.beginUpdate();
53741 reader.add("north", new CP("north", "North"));
53742 reader.add("west", new CP("west", {title: "West"}));
53743 reader.add("east", new CP("east", {title: "East"}));
53744
53745 reader.regions.listView.add(new CP("listView", "List"));
53746 reader.regions.preview.add(new CP("preview", "Preview"));
53747 reader.endUpdate();
53748 </code></pre>
53749 * @constructor
53750 * Create a new ReaderLayout
53751 * @param {Object} config Configuration options
53752 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53753 * document.body if omitted)
53754 */
53755 Roo.ReaderLayout = function(config, renderTo){
53756     var c = config || {size:{}};
53757     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53758         north: c.north !== false ? Roo.apply({
53759             split:false,
53760             initialSize: 32,
53761             titlebar: false
53762         }, c.north) : false,
53763         west: c.west !== false ? Roo.apply({
53764             split:true,
53765             initialSize: 200,
53766             minSize: 175,
53767             maxSize: 400,
53768             titlebar: true,
53769             collapsible: true,
53770             animate: true,
53771             margins:{left:5,right:0,bottom:5,top:5},
53772             cmargins:{left:5,right:5,bottom:5,top:5}
53773         }, c.west) : false,
53774         east: c.east !== false ? Roo.apply({
53775             split:true,
53776             initialSize: 200,
53777             minSize: 175,
53778             maxSize: 400,
53779             titlebar: true,
53780             collapsible: true,
53781             animate: true,
53782             margins:{left:0,right:5,bottom:5,top:5},
53783             cmargins:{left:5,right:5,bottom:5,top:5}
53784         }, c.east) : false,
53785         center: Roo.apply({
53786             tabPosition: 'top',
53787             autoScroll:false,
53788             closeOnTab: true,
53789             titlebar:false,
53790             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53791         }, c.center)
53792     });
53793
53794     this.el.addClass('x-reader');
53795
53796     this.beginUpdate();
53797
53798     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53799         south: c.preview !== false ? Roo.apply({
53800             split:true,
53801             initialSize: 200,
53802             minSize: 100,
53803             autoScroll:true,
53804             collapsible:true,
53805             titlebar: true,
53806             cmargins:{top:5,left:0, right:0, bottom:0}
53807         }, c.preview) : false,
53808         center: Roo.apply({
53809             autoScroll:false,
53810             titlebar:false,
53811             minHeight:200
53812         }, c.listView)
53813     });
53814     this.add('center', new Roo.NestedLayoutPanel(inner,
53815             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53816
53817     this.endUpdate();
53818
53819     this.regions.preview = inner.getRegion('south');
53820     this.regions.listView = inner.getRegion('center');
53821 };
53822
53823 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53824  * Based on:
53825  * Ext JS Library 1.1.1
53826  * Copyright(c) 2006-2007, Ext JS, LLC.
53827  *
53828  * Originally Released Under LGPL - original licence link has changed is not relivant.
53829  *
53830  * Fork - LGPL
53831  * <script type="text/javascript">
53832  */
53833  
53834 /**
53835  * @class Roo.grid.Grid
53836  * @extends Roo.util.Observable
53837  * This class represents the primary interface of a component based grid control.
53838  * <br><br>Usage:<pre><code>
53839  var grid = new Roo.grid.Grid("my-container-id", {
53840      ds: myDataStore,
53841      cm: myColModel,
53842      selModel: mySelectionModel,
53843      autoSizeColumns: true,
53844      monitorWindowResize: false,
53845      trackMouseOver: true
53846  });
53847  // set any options
53848  grid.render();
53849  * </code></pre>
53850  * <b>Common Problems:</b><br/>
53851  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53852  * element will correct this<br/>
53853  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53854  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53855  * are unpredictable.<br/>
53856  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53857  * grid to calculate dimensions/offsets.<br/>
53858   * @constructor
53859  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53860  * The container MUST have some type of size defined for the grid to fill. The container will be
53861  * automatically set to position relative if it isn't already.
53862  * @param {Object} config A config object that sets properties on this grid.
53863  */
53864 Roo.grid.Grid = function(container, config){
53865         // initialize the container
53866         this.container = Roo.get(container);
53867         this.container.update("");
53868         this.container.setStyle("overflow", "hidden");
53869     this.container.addClass('x-grid-container');
53870
53871     this.id = this.container.id;
53872
53873     Roo.apply(this, config);
53874     // check and correct shorthanded configs
53875     if(this.ds){
53876         this.dataSource = this.ds;
53877         delete this.ds;
53878     }
53879     if(this.cm){
53880         this.colModel = this.cm;
53881         delete this.cm;
53882     }
53883     if(this.sm){
53884         this.selModel = this.sm;
53885         delete this.sm;
53886     }
53887
53888     if (this.selModel) {
53889         this.selModel = Roo.factory(this.selModel, Roo.grid);
53890         this.sm = this.selModel;
53891         this.sm.xmodule = this.xmodule || false;
53892     }
53893     if (typeof(this.colModel.config) == 'undefined') {
53894         this.colModel = new Roo.grid.ColumnModel(this.colModel);
53895         this.cm = this.colModel;
53896         this.cm.xmodule = this.xmodule || false;
53897     }
53898     if (this.dataSource) {
53899         this.dataSource= Roo.factory(this.dataSource, Roo.data);
53900         this.ds = this.dataSource;
53901         this.ds.xmodule = this.xmodule || false;
53902          
53903     }
53904     
53905     
53906     
53907     if(this.width){
53908         this.container.setWidth(this.width);
53909     }
53910
53911     if(this.height){
53912         this.container.setHeight(this.height);
53913     }
53914     /** @private */
53915         this.addEvents({
53916         // raw events
53917         /**
53918          * @event click
53919          * The raw click event for the entire grid.
53920          * @param {Roo.EventObject} e
53921          */
53922         "click" : true,
53923         /**
53924          * @event dblclick
53925          * The raw dblclick event for the entire grid.
53926          * @param {Roo.EventObject} e
53927          */
53928         "dblclick" : true,
53929         /**
53930          * @event contextmenu
53931          * The raw contextmenu event for the entire grid.
53932          * @param {Roo.EventObject} e
53933          */
53934         "contextmenu" : true,
53935         /**
53936          * @event mousedown
53937          * The raw mousedown event for the entire grid.
53938          * @param {Roo.EventObject} e
53939          */
53940         "mousedown" : true,
53941         /**
53942          * @event mouseup
53943          * The raw mouseup event for the entire grid.
53944          * @param {Roo.EventObject} e
53945          */
53946         "mouseup" : true,
53947         /**
53948          * @event mouseover
53949          * The raw mouseover event for the entire grid.
53950          * @param {Roo.EventObject} e
53951          */
53952         "mouseover" : true,
53953         /**
53954          * @event mouseout
53955          * The raw mouseout event for the entire grid.
53956          * @param {Roo.EventObject} e
53957          */
53958         "mouseout" : true,
53959         /**
53960          * @event keypress
53961          * The raw keypress event for the entire grid.
53962          * @param {Roo.EventObject} e
53963          */
53964         "keypress" : true,
53965         /**
53966          * @event keydown
53967          * The raw keydown event for the entire grid.
53968          * @param {Roo.EventObject} e
53969          */
53970         "keydown" : true,
53971
53972         // custom events
53973
53974         /**
53975          * @event cellclick
53976          * Fires when a cell is clicked
53977          * @param {Grid} this
53978          * @param {Number} rowIndex
53979          * @param {Number} columnIndex
53980          * @param {Roo.EventObject} e
53981          */
53982         "cellclick" : true,
53983         /**
53984          * @event celldblclick
53985          * Fires when a cell is double clicked
53986          * @param {Grid} this
53987          * @param {Number} rowIndex
53988          * @param {Number} columnIndex
53989          * @param {Roo.EventObject} e
53990          */
53991         "celldblclick" : true,
53992         /**
53993          * @event rowclick
53994          * Fires when a row is clicked
53995          * @param {Grid} this
53996          * @param {Number} rowIndex
53997          * @param {Roo.EventObject} e
53998          */
53999         "rowclick" : true,
54000         /**
54001          * @event rowdblclick
54002          * Fires when a row is double clicked
54003          * @param {Grid} this
54004          * @param {Number} rowIndex
54005          * @param {Roo.EventObject} e
54006          */
54007         "rowdblclick" : true,
54008         /**
54009          * @event headerclick
54010          * Fires when a header is clicked
54011          * @param {Grid} this
54012          * @param {Number} columnIndex
54013          * @param {Roo.EventObject} e
54014          */
54015         "headerclick" : true,
54016         /**
54017          * @event headerdblclick
54018          * Fires when a header cell is double clicked
54019          * @param {Grid} this
54020          * @param {Number} columnIndex
54021          * @param {Roo.EventObject} e
54022          */
54023         "headerdblclick" : true,
54024         /**
54025          * @event rowcontextmenu
54026          * Fires when a row is right clicked
54027          * @param {Grid} this
54028          * @param {Number} rowIndex
54029          * @param {Roo.EventObject} e
54030          */
54031         "rowcontextmenu" : true,
54032         /**
54033          * @event cellcontextmenu
54034          * Fires when a cell is right clicked
54035          * @param {Grid} this
54036          * @param {Number} rowIndex
54037          * @param {Number} cellIndex
54038          * @param {Roo.EventObject} e
54039          */
54040          "cellcontextmenu" : true,
54041         /**
54042          * @event headercontextmenu
54043          * Fires when a header is right clicked
54044          * @param {Grid} this
54045          * @param {Number} columnIndex
54046          * @param {Roo.EventObject} e
54047          */
54048         "headercontextmenu" : true,
54049         /**
54050          * @event bodyscroll
54051          * Fires when the body element is scrolled
54052          * @param {Number} scrollLeft
54053          * @param {Number} scrollTop
54054          */
54055         "bodyscroll" : true,
54056         /**
54057          * @event columnresize
54058          * Fires when the user resizes a column
54059          * @param {Number} columnIndex
54060          * @param {Number} newSize
54061          */
54062         "columnresize" : true,
54063         /**
54064          * @event columnmove
54065          * Fires when the user moves a column
54066          * @param {Number} oldIndex
54067          * @param {Number} newIndex
54068          */
54069         "columnmove" : true,
54070         /**
54071          * @event startdrag
54072          * Fires when row(s) start being dragged
54073          * @param {Grid} this
54074          * @param {Roo.GridDD} dd The drag drop object
54075          * @param {event} e The raw browser event
54076          */
54077         "startdrag" : true,
54078         /**
54079          * @event enddrag
54080          * Fires when a drag operation is complete
54081          * @param {Grid} this
54082          * @param {Roo.GridDD} dd The drag drop object
54083          * @param {event} e The raw browser event
54084          */
54085         "enddrag" : true,
54086         /**
54087          * @event dragdrop
54088          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
54095         /**
54096          * @event dragover
54097          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
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         "dragover" : true,
54104         /**
54105          * @event dragenter
54106          *  Fires when the dragged row(s) first cross another DD target while being dragged
54107          * @param {Grid} this
54108          * @param {Roo.GridDD} dd The drag drop object
54109          * @param {String} targetId The target drag drop object
54110          * @param {event} e The raw browser event
54111          */
54112         "dragenter" : true,
54113         /**
54114          * @event dragout
54115          * Fires when the dragged row(s) leave another DD target while being dragged
54116          * @param {Grid} this
54117          * @param {Roo.GridDD} dd The drag drop object
54118          * @param {String} targetId The target drag drop object
54119          * @param {event} e The raw browser event
54120          */
54121         "dragout" : true,
54122         /**
54123          * @event rowclass
54124          * Fires when a row is rendered, so you can change add a style to it.
54125          * @param {GridView} gridview   The grid view
54126          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54127          */
54128         'rowclass' : true,
54129
54130         /**
54131          * @event render
54132          * Fires when the grid is rendered
54133          * @param {Grid} grid
54134          */
54135         'render' : true
54136     });
54137
54138     Roo.grid.Grid.superclass.constructor.call(this);
54139 };
54140 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54141     
54142     /**
54143      * @cfg {String} ddGroup - drag drop group.
54144      */
54145
54146     /**
54147      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54148      */
54149     minColumnWidth : 25,
54150
54151     /**
54152      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54153      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54154      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54155      */
54156     autoSizeColumns : false,
54157
54158     /**
54159      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54160      */
54161     autoSizeHeaders : true,
54162
54163     /**
54164      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54165      */
54166     monitorWindowResize : true,
54167
54168     /**
54169      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54170      * rows measured to get a columns size. Default is 0 (all rows).
54171      */
54172     maxRowsToMeasure : 0,
54173
54174     /**
54175      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54176      */
54177     trackMouseOver : true,
54178
54179     /**
54180     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54181     */
54182     
54183     /**
54184     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54185     */
54186     enableDragDrop : false,
54187     
54188     /**
54189     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54190     */
54191     enableColumnMove : true,
54192     
54193     /**
54194     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54195     */
54196     enableColumnHide : true,
54197     
54198     /**
54199     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54200     */
54201     enableRowHeightSync : false,
54202     
54203     /**
54204     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54205     */
54206     stripeRows : true,
54207     
54208     /**
54209     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54210     */
54211     autoHeight : false,
54212
54213     /**
54214      * @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.
54215      */
54216     autoExpandColumn : false,
54217
54218     /**
54219     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54220     * Default is 50.
54221     */
54222     autoExpandMin : 50,
54223
54224     /**
54225     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54226     */
54227     autoExpandMax : 1000,
54228
54229     /**
54230     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54231     */
54232     view : null,
54233
54234     /**
54235     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54236     */
54237     loadMask : false,
54238     /**
54239     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54240     */
54241     dropTarget: false,
54242     
54243    
54244     
54245     // private
54246     rendered : false,
54247
54248     /**
54249     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54250     * of a fixed width. Default is false.
54251     */
54252     /**
54253     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54254     */
54255     /**
54256      * Called once after all setup has been completed and the grid is ready to be rendered.
54257      * @return {Roo.grid.Grid} this
54258      */
54259     render : function()
54260     {
54261         var c = this.container;
54262         // try to detect autoHeight/width mode
54263         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54264             this.autoHeight = true;
54265         }
54266         var view = this.getView();
54267         view.init(this);
54268
54269         c.on("click", this.onClick, this);
54270         c.on("dblclick", this.onDblClick, this);
54271         c.on("contextmenu", this.onContextMenu, this);
54272         c.on("keydown", this.onKeyDown, this);
54273         if (Roo.isTouch) {
54274             c.on("touchstart", this.onTouchStart, this);
54275         }
54276
54277         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54278
54279         this.getSelectionModel().init(this);
54280
54281         view.render();
54282
54283         if(this.loadMask){
54284             this.loadMask = new Roo.LoadMask(this.container,
54285                     Roo.apply({store:this.dataSource}, this.loadMask));
54286         }
54287         
54288         
54289         if (this.toolbar && this.toolbar.xtype) {
54290             this.toolbar.container = this.getView().getHeaderPanel(true);
54291             this.toolbar = new Roo.Toolbar(this.toolbar);
54292         }
54293         if (this.footer && this.footer.xtype) {
54294             this.footer.dataSource = this.getDataSource();
54295             this.footer.container = this.getView().getFooterPanel(true);
54296             this.footer = Roo.factory(this.footer, Roo);
54297         }
54298         if (this.dropTarget && this.dropTarget.xtype) {
54299             delete this.dropTarget.xtype;
54300             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54301         }
54302         
54303         
54304         this.rendered = true;
54305         this.fireEvent('render', this);
54306         return this;
54307     },
54308
54309         /**
54310          * Reconfigures the grid to use a different Store and Column Model.
54311          * The View will be bound to the new objects and refreshed.
54312          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54313          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54314          */
54315     reconfigure : function(dataSource, colModel){
54316         if(this.loadMask){
54317             this.loadMask.destroy();
54318             this.loadMask = new Roo.LoadMask(this.container,
54319                     Roo.apply({store:dataSource}, this.loadMask));
54320         }
54321         this.view.bind(dataSource, colModel);
54322         this.dataSource = dataSource;
54323         this.colModel = colModel;
54324         this.view.refresh(true);
54325     },
54326
54327     // private
54328     onKeyDown : function(e){
54329         this.fireEvent("keydown", e);
54330     },
54331
54332     /**
54333      * Destroy this grid.
54334      * @param {Boolean} removeEl True to remove the element
54335      */
54336     destroy : function(removeEl, keepListeners){
54337         if(this.loadMask){
54338             this.loadMask.destroy();
54339         }
54340         var c = this.container;
54341         c.removeAllListeners();
54342         this.view.destroy();
54343         this.colModel.purgeListeners();
54344         if(!keepListeners){
54345             this.purgeListeners();
54346         }
54347         c.update("");
54348         if(removeEl === true){
54349             c.remove();
54350         }
54351     },
54352
54353     // private
54354     processEvent : function(name, e){
54355         // does this fire select???
54356         //Roo.log('grid:processEvent '  + name);
54357         
54358         if (name != 'touchstart' ) {
54359             this.fireEvent(name, e);    
54360         }
54361         
54362         var t = e.getTarget();
54363         var v = this.view;
54364         var header = v.findHeaderIndex(t);
54365         if(header !== false){
54366             var ename = name == 'touchstart' ? 'click' : name;
54367              
54368             this.fireEvent("header" + ename, this, header, e);
54369         }else{
54370             var row = v.findRowIndex(t);
54371             var cell = v.findCellIndex(t);
54372             if (name == 'touchstart') {
54373                 // first touch is always a click.
54374                 // hopefull this happens after selection is updated.?
54375                 name = false;
54376                 
54377                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54378                     var cs = this.selModel.getSelectedCell();
54379                     if (row == cs[0] && cell == cs[1]){
54380                         name = 'dblclick';
54381                     }
54382                 }
54383                 if (typeof(this.selModel.getSelections) != 'undefined') {
54384                     var cs = this.selModel.getSelections();
54385                     var ds = this.dataSource;
54386                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54387                         name = 'dblclick';
54388                     }
54389                 }
54390                 if (!name) {
54391                     return;
54392                 }
54393             }
54394             
54395             
54396             if(row !== false){
54397                 this.fireEvent("row" + name, this, row, e);
54398                 if(cell !== false){
54399                     this.fireEvent("cell" + name, this, row, cell, e);
54400                 }
54401             }
54402         }
54403     },
54404
54405     // private
54406     onClick : function(e){
54407         this.processEvent("click", e);
54408     },
54409    // private
54410     onTouchStart : function(e){
54411         this.processEvent("touchstart", e);
54412     },
54413
54414     // private
54415     onContextMenu : function(e, t){
54416         this.processEvent("contextmenu", e);
54417     },
54418
54419     // private
54420     onDblClick : function(e){
54421         this.processEvent("dblclick", e);
54422     },
54423
54424     // private
54425     walkCells : function(row, col, step, fn, scope){
54426         var cm = this.colModel, clen = cm.getColumnCount();
54427         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54428         if(step < 0){
54429             if(col < 0){
54430                 row--;
54431                 first = false;
54432             }
54433             while(row >= 0){
54434                 if(!first){
54435                     col = clen-1;
54436                 }
54437                 first = false;
54438                 while(col >= 0){
54439                     if(fn.call(scope || this, row, col, cm) === true){
54440                         return [row, col];
54441                     }
54442                     col--;
54443                 }
54444                 row--;
54445             }
54446         } else {
54447             if(col >= clen){
54448                 row++;
54449                 first = false;
54450             }
54451             while(row < rlen){
54452                 if(!first){
54453                     col = 0;
54454                 }
54455                 first = false;
54456                 while(col < clen){
54457                     if(fn.call(scope || this, row, col, cm) === true){
54458                         return [row, col];
54459                     }
54460                     col++;
54461                 }
54462                 row++;
54463             }
54464         }
54465         return null;
54466     },
54467
54468     // private
54469     getSelections : function(){
54470         return this.selModel.getSelections();
54471     },
54472
54473     /**
54474      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54475      * but if manual update is required this method will initiate it.
54476      */
54477     autoSize : function(){
54478         if(this.rendered){
54479             this.view.layout();
54480             if(this.view.adjustForScroll){
54481                 this.view.adjustForScroll();
54482             }
54483         }
54484     },
54485
54486     /**
54487      * Returns the grid's underlying element.
54488      * @return {Element} The element
54489      */
54490     getGridEl : function(){
54491         return this.container;
54492     },
54493
54494     // private for compatibility, overridden by editor grid
54495     stopEditing : function(){},
54496
54497     /**
54498      * Returns the grid's SelectionModel.
54499      * @return {SelectionModel}
54500      */
54501     getSelectionModel : function(){
54502         if(!this.selModel){
54503             this.selModel = new Roo.grid.RowSelectionModel();
54504         }
54505         return this.selModel;
54506     },
54507
54508     /**
54509      * Returns the grid's DataSource.
54510      * @return {DataSource}
54511      */
54512     getDataSource : function(){
54513         return this.dataSource;
54514     },
54515
54516     /**
54517      * Returns the grid's ColumnModel.
54518      * @return {ColumnModel}
54519      */
54520     getColumnModel : function(){
54521         return this.colModel;
54522     },
54523
54524     /**
54525      * Returns the grid's GridView object.
54526      * @return {GridView}
54527      */
54528     getView : function(){
54529         if(!this.view){
54530             this.view = new Roo.grid.GridView(this.viewConfig);
54531         }
54532         return this.view;
54533     },
54534     /**
54535      * Called to get grid's drag proxy text, by default returns this.ddText.
54536      * @return {String}
54537      */
54538     getDragDropText : function(){
54539         var count = this.selModel.getCount();
54540         return String.format(this.ddText, count, count == 1 ? '' : 's');
54541     }
54542 });
54543 /**
54544  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54545  * %0 is replaced with the number of selected rows.
54546  * @type String
54547  */
54548 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54549  * Based on:
54550  * Ext JS Library 1.1.1
54551  * Copyright(c) 2006-2007, Ext JS, LLC.
54552  *
54553  * Originally Released Under LGPL - original licence link has changed is not relivant.
54554  *
54555  * Fork - LGPL
54556  * <script type="text/javascript">
54557  */
54558  
54559 Roo.grid.AbstractGridView = function(){
54560         this.grid = null;
54561         
54562         this.events = {
54563             "beforerowremoved" : true,
54564             "beforerowsinserted" : true,
54565             "beforerefresh" : true,
54566             "rowremoved" : true,
54567             "rowsinserted" : true,
54568             "rowupdated" : true,
54569             "refresh" : true
54570         };
54571     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54572 };
54573
54574 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54575     rowClass : "x-grid-row",
54576     cellClass : "x-grid-cell",
54577     tdClass : "x-grid-td",
54578     hdClass : "x-grid-hd",
54579     splitClass : "x-grid-hd-split",
54580     
54581     init: function(grid){
54582         this.grid = grid;
54583                 var cid = this.grid.getGridEl().id;
54584         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54585         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54586         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54587         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54588         },
54589         
54590     getColumnRenderers : function(){
54591         var renderers = [];
54592         var cm = this.grid.colModel;
54593         var colCount = cm.getColumnCount();
54594         for(var i = 0; i < colCount; i++){
54595             renderers[i] = cm.getRenderer(i);
54596         }
54597         return renderers;
54598     },
54599     
54600     getColumnIds : function(){
54601         var ids = [];
54602         var cm = this.grid.colModel;
54603         var colCount = cm.getColumnCount();
54604         for(var i = 0; i < colCount; i++){
54605             ids[i] = cm.getColumnId(i);
54606         }
54607         return ids;
54608     },
54609     
54610     getDataIndexes : function(){
54611         if(!this.indexMap){
54612             this.indexMap = this.buildIndexMap();
54613         }
54614         return this.indexMap.colToData;
54615     },
54616     
54617     getColumnIndexByDataIndex : function(dataIndex){
54618         if(!this.indexMap){
54619             this.indexMap = this.buildIndexMap();
54620         }
54621         return this.indexMap.dataToCol[dataIndex];
54622     },
54623     
54624     /**
54625      * Set a css style for a column dynamically. 
54626      * @param {Number} colIndex The index of the column
54627      * @param {String} name The css property name
54628      * @param {String} value The css value
54629      */
54630     setCSSStyle : function(colIndex, name, value){
54631         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54632         Roo.util.CSS.updateRule(selector, name, value);
54633     },
54634     
54635     generateRules : function(cm){
54636         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54637         Roo.util.CSS.removeStyleSheet(rulesId);
54638         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54639             var cid = cm.getColumnId(i);
54640             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54641                          this.tdSelector, cid, " {\n}\n",
54642                          this.hdSelector, cid, " {\n}\n",
54643                          this.splitSelector, cid, " {\n}\n");
54644         }
54645         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54646     }
54647 });/*
54648  * Based on:
54649  * Ext JS Library 1.1.1
54650  * Copyright(c) 2006-2007, Ext JS, LLC.
54651  *
54652  * Originally Released Under LGPL - original licence link has changed is not relivant.
54653  *
54654  * Fork - LGPL
54655  * <script type="text/javascript">
54656  */
54657
54658 // private
54659 // This is a support class used internally by the Grid components
54660 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54661     this.grid = grid;
54662     this.view = grid.getView();
54663     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54664     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54665     if(hd2){
54666         this.setHandleElId(Roo.id(hd));
54667         this.setOuterHandleElId(Roo.id(hd2));
54668     }
54669     this.scroll = false;
54670 };
54671 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54672     maxDragWidth: 120,
54673     getDragData : function(e){
54674         var t = Roo.lib.Event.getTarget(e);
54675         var h = this.view.findHeaderCell(t);
54676         if(h){
54677             return {ddel: h.firstChild, header:h};
54678         }
54679         return false;
54680     },
54681
54682     onInitDrag : function(e){
54683         this.view.headersDisabled = true;
54684         var clone = this.dragData.ddel.cloneNode(true);
54685         clone.id = Roo.id();
54686         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54687         this.proxy.update(clone);
54688         return true;
54689     },
54690
54691     afterValidDrop : function(){
54692         var v = this.view;
54693         setTimeout(function(){
54694             v.headersDisabled = false;
54695         }, 50);
54696     },
54697
54698     afterInvalidDrop : function(){
54699         var v = this.view;
54700         setTimeout(function(){
54701             v.headersDisabled = false;
54702         }, 50);
54703     }
54704 });
54705 /*
54706  * Based on:
54707  * Ext JS Library 1.1.1
54708  * Copyright(c) 2006-2007, Ext JS, LLC.
54709  *
54710  * Originally Released Under LGPL - original licence link has changed is not relivant.
54711  *
54712  * Fork - LGPL
54713  * <script type="text/javascript">
54714  */
54715 // private
54716 // This is a support class used internally by the Grid components
54717 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54718     this.grid = grid;
54719     this.view = grid.getView();
54720     // split the proxies so they don't interfere with mouse events
54721     this.proxyTop = Roo.DomHelper.append(document.body, {
54722         cls:"col-move-top", html:"&#160;"
54723     }, true);
54724     this.proxyBottom = Roo.DomHelper.append(document.body, {
54725         cls:"col-move-bottom", html:"&#160;"
54726     }, true);
54727     this.proxyTop.hide = this.proxyBottom.hide = function(){
54728         this.setLeftTop(-100,-100);
54729         this.setStyle("visibility", "hidden");
54730     };
54731     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54732     // temporarily disabled
54733     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54734     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54735 };
54736 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54737     proxyOffsets : [-4, -9],
54738     fly: Roo.Element.fly,
54739
54740     getTargetFromEvent : function(e){
54741         var t = Roo.lib.Event.getTarget(e);
54742         var cindex = this.view.findCellIndex(t);
54743         if(cindex !== false){
54744             return this.view.getHeaderCell(cindex);
54745         }
54746         return null;
54747     },
54748
54749     nextVisible : function(h){
54750         var v = this.view, cm = this.grid.colModel;
54751         h = h.nextSibling;
54752         while(h){
54753             if(!cm.isHidden(v.getCellIndex(h))){
54754                 return h;
54755             }
54756             h = h.nextSibling;
54757         }
54758         return null;
54759     },
54760
54761     prevVisible : function(h){
54762         var v = this.view, cm = this.grid.colModel;
54763         h = h.prevSibling;
54764         while(h){
54765             if(!cm.isHidden(v.getCellIndex(h))){
54766                 return h;
54767             }
54768             h = h.prevSibling;
54769         }
54770         return null;
54771     },
54772
54773     positionIndicator : function(h, n, e){
54774         var x = Roo.lib.Event.getPageX(e);
54775         var r = Roo.lib.Dom.getRegion(n.firstChild);
54776         var px, pt, py = r.top + this.proxyOffsets[1];
54777         if((r.right - x) <= (r.right-r.left)/2){
54778             px = r.right+this.view.borderWidth;
54779             pt = "after";
54780         }else{
54781             px = r.left;
54782             pt = "before";
54783         }
54784         var oldIndex = this.view.getCellIndex(h);
54785         var newIndex = this.view.getCellIndex(n);
54786
54787         if(this.grid.colModel.isFixed(newIndex)){
54788             return false;
54789         }
54790
54791         var locked = this.grid.colModel.isLocked(newIndex);
54792
54793         if(pt == "after"){
54794             newIndex++;
54795         }
54796         if(oldIndex < newIndex){
54797             newIndex--;
54798         }
54799         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54800             return false;
54801         }
54802         px +=  this.proxyOffsets[0];
54803         this.proxyTop.setLeftTop(px, py);
54804         this.proxyTop.show();
54805         if(!this.bottomOffset){
54806             this.bottomOffset = this.view.mainHd.getHeight();
54807         }
54808         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54809         this.proxyBottom.show();
54810         return pt;
54811     },
54812
54813     onNodeEnter : function(n, dd, e, data){
54814         if(data.header != n){
54815             this.positionIndicator(data.header, n, e);
54816         }
54817     },
54818
54819     onNodeOver : function(n, dd, e, data){
54820         var result = false;
54821         if(data.header != n){
54822             result = this.positionIndicator(data.header, n, e);
54823         }
54824         if(!result){
54825             this.proxyTop.hide();
54826             this.proxyBottom.hide();
54827         }
54828         return result ? this.dropAllowed : this.dropNotAllowed;
54829     },
54830
54831     onNodeOut : function(n, dd, e, data){
54832         this.proxyTop.hide();
54833         this.proxyBottom.hide();
54834     },
54835
54836     onNodeDrop : function(n, dd, e, data){
54837         var h = data.header;
54838         if(h != n){
54839             var cm = this.grid.colModel;
54840             var x = Roo.lib.Event.getPageX(e);
54841             var r = Roo.lib.Dom.getRegion(n.firstChild);
54842             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54843             var oldIndex = this.view.getCellIndex(h);
54844             var newIndex = this.view.getCellIndex(n);
54845             var locked = cm.isLocked(newIndex);
54846             if(pt == "after"){
54847                 newIndex++;
54848             }
54849             if(oldIndex < newIndex){
54850                 newIndex--;
54851             }
54852             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54853                 return false;
54854             }
54855             cm.setLocked(oldIndex, locked, true);
54856             cm.moveColumn(oldIndex, newIndex);
54857             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54858             return true;
54859         }
54860         return false;
54861     }
54862 });
54863 /*
54864  * Based on:
54865  * Ext JS Library 1.1.1
54866  * Copyright(c) 2006-2007, Ext JS, LLC.
54867  *
54868  * Originally Released Under LGPL - original licence link has changed is not relivant.
54869  *
54870  * Fork - LGPL
54871  * <script type="text/javascript">
54872  */
54873   
54874 /**
54875  * @class Roo.grid.GridView
54876  * @extends Roo.util.Observable
54877  *
54878  * @constructor
54879  * @param {Object} config
54880  */
54881 Roo.grid.GridView = function(config){
54882     Roo.grid.GridView.superclass.constructor.call(this);
54883     this.el = null;
54884
54885     Roo.apply(this, config);
54886 };
54887
54888 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
54889
54890     unselectable :  'unselectable="on"',
54891     unselectableCls :  'x-unselectable',
54892     
54893     
54894     rowClass : "x-grid-row",
54895
54896     cellClass : "x-grid-col",
54897
54898     tdClass : "x-grid-td",
54899
54900     hdClass : "x-grid-hd",
54901
54902     splitClass : "x-grid-split",
54903
54904     sortClasses : ["sort-asc", "sort-desc"],
54905
54906     enableMoveAnim : false,
54907
54908     hlColor: "C3DAF9",
54909
54910     dh : Roo.DomHelper,
54911
54912     fly : Roo.Element.fly,
54913
54914     css : Roo.util.CSS,
54915
54916     borderWidth: 1,
54917
54918     splitOffset: 3,
54919
54920     scrollIncrement : 22,
54921
54922     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
54923
54924     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
54925
54926     bind : function(ds, cm){
54927         if(this.ds){
54928             this.ds.un("load", this.onLoad, this);
54929             this.ds.un("datachanged", this.onDataChange, this);
54930             this.ds.un("add", this.onAdd, this);
54931             this.ds.un("remove", this.onRemove, this);
54932             this.ds.un("update", this.onUpdate, this);
54933             this.ds.un("clear", this.onClear, this);
54934         }
54935         if(ds){
54936             ds.on("load", this.onLoad, this);
54937             ds.on("datachanged", this.onDataChange, this);
54938             ds.on("add", this.onAdd, this);
54939             ds.on("remove", this.onRemove, this);
54940             ds.on("update", this.onUpdate, this);
54941             ds.on("clear", this.onClear, this);
54942         }
54943         this.ds = ds;
54944
54945         if(this.cm){
54946             this.cm.un("widthchange", this.onColWidthChange, this);
54947             this.cm.un("headerchange", this.onHeaderChange, this);
54948             this.cm.un("hiddenchange", this.onHiddenChange, this);
54949             this.cm.un("columnmoved", this.onColumnMove, this);
54950             this.cm.un("columnlockchange", this.onColumnLock, this);
54951         }
54952         if(cm){
54953             this.generateRules(cm);
54954             cm.on("widthchange", this.onColWidthChange, this);
54955             cm.on("headerchange", this.onHeaderChange, this);
54956             cm.on("hiddenchange", this.onHiddenChange, this);
54957             cm.on("columnmoved", this.onColumnMove, this);
54958             cm.on("columnlockchange", this.onColumnLock, this);
54959         }
54960         this.cm = cm;
54961     },
54962
54963     init: function(grid){
54964         Roo.grid.GridView.superclass.init.call(this, grid);
54965
54966         this.bind(grid.dataSource, grid.colModel);
54967
54968         grid.on("headerclick", this.handleHeaderClick, this);
54969
54970         if(grid.trackMouseOver){
54971             grid.on("mouseover", this.onRowOver, this);
54972             grid.on("mouseout", this.onRowOut, this);
54973         }
54974         grid.cancelTextSelection = function(){};
54975         this.gridId = grid.id;
54976
54977         var tpls = this.templates || {};
54978
54979         if(!tpls.master){
54980             tpls.master = new Roo.Template(
54981                '<div class="x-grid" hidefocus="true">',
54982                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
54983                   '<div class="x-grid-topbar"></div>',
54984                   '<div class="x-grid-scroller"><div></div></div>',
54985                   '<div class="x-grid-locked">',
54986                       '<div class="x-grid-header">{lockedHeader}</div>',
54987                       '<div class="x-grid-body">{lockedBody}</div>',
54988                   "</div>",
54989                   '<div class="x-grid-viewport">',
54990                       '<div class="x-grid-header">{header}</div>',
54991                       '<div class="x-grid-body">{body}</div>',
54992                   "</div>",
54993                   '<div class="x-grid-bottombar"></div>',
54994                  
54995                   '<div class="x-grid-resize-proxy">&#160;</div>',
54996                "</div>"
54997             );
54998             tpls.master.disableformats = true;
54999         }
55000
55001         if(!tpls.header){
55002             tpls.header = new Roo.Template(
55003                '<table border="0" cellspacing="0" cellpadding="0">',
55004                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55005                "</table>{splits}"
55006             );
55007             tpls.header.disableformats = true;
55008         }
55009         tpls.header.compile();
55010
55011         if(!tpls.hcell){
55012             tpls.hcell = new Roo.Template(
55013                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55014                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55015                 "</div></td>"
55016              );
55017              tpls.hcell.disableFormats = true;
55018         }
55019         tpls.hcell.compile();
55020
55021         if(!tpls.hsplit){
55022             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55023                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55024             tpls.hsplit.disableFormats = true;
55025         }
55026         tpls.hsplit.compile();
55027
55028         if(!tpls.body){
55029             tpls.body = new Roo.Template(
55030                '<table border="0" cellspacing="0" cellpadding="0">',
55031                "<tbody>{rows}</tbody>",
55032                "</table>"
55033             );
55034             tpls.body.disableFormats = true;
55035         }
55036         tpls.body.compile();
55037
55038         if(!tpls.row){
55039             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55040             tpls.row.disableFormats = true;
55041         }
55042         tpls.row.compile();
55043
55044         if(!tpls.cell){
55045             tpls.cell = new Roo.Template(
55046                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55047                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55048                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55049                 "</td>"
55050             );
55051             tpls.cell.disableFormats = true;
55052         }
55053         tpls.cell.compile();
55054
55055         this.templates = tpls;
55056     },
55057
55058     // remap these for backwards compat
55059     onColWidthChange : function(){
55060         this.updateColumns.apply(this, arguments);
55061     },
55062     onHeaderChange : function(){
55063         this.updateHeaders.apply(this, arguments);
55064     }, 
55065     onHiddenChange : function(){
55066         this.handleHiddenChange.apply(this, arguments);
55067     },
55068     onColumnMove : function(){
55069         this.handleColumnMove.apply(this, arguments);
55070     },
55071     onColumnLock : function(){
55072         this.handleLockChange.apply(this, arguments);
55073     },
55074
55075     onDataChange : function(){
55076         this.refresh();
55077         this.updateHeaderSortState();
55078     },
55079
55080     onClear : function(){
55081         this.refresh();
55082     },
55083
55084     onUpdate : function(ds, record){
55085         this.refreshRow(record);
55086     },
55087
55088     refreshRow : function(record){
55089         var ds = this.ds, index;
55090         if(typeof record == 'number'){
55091             index = record;
55092             record = ds.getAt(index);
55093         }else{
55094             index = ds.indexOf(record);
55095         }
55096         this.insertRows(ds, index, index, true);
55097         this.onRemove(ds, record, index+1, true);
55098         this.syncRowHeights(index, index);
55099         this.layout();
55100         this.fireEvent("rowupdated", this, index, record);
55101     },
55102
55103     onAdd : function(ds, records, index){
55104         this.insertRows(ds, index, index + (records.length-1));
55105     },
55106
55107     onRemove : function(ds, record, index, isUpdate){
55108         if(isUpdate !== true){
55109             this.fireEvent("beforerowremoved", this, index, record);
55110         }
55111         var bt = this.getBodyTable(), lt = this.getLockedTable();
55112         if(bt.rows[index]){
55113             bt.firstChild.removeChild(bt.rows[index]);
55114         }
55115         if(lt.rows[index]){
55116             lt.firstChild.removeChild(lt.rows[index]);
55117         }
55118         if(isUpdate !== true){
55119             this.stripeRows(index);
55120             this.syncRowHeights(index, index);
55121             this.layout();
55122             this.fireEvent("rowremoved", this, index, record);
55123         }
55124     },
55125
55126     onLoad : function(){
55127         this.scrollToTop();
55128     },
55129
55130     /**
55131      * Scrolls the grid to the top
55132      */
55133     scrollToTop : function(){
55134         if(this.scroller){
55135             this.scroller.dom.scrollTop = 0;
55136             this.syncScroll();
55137         }
55138     },
55139
55140     /**
55141      * Gets a panel in the header of the grid that can be used for toolbars etc.
55142      * After modifying the contents of this panel a call to grid.autoSize() may be
55143      * required to register any changes in size.
55144      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55145      * @return Roo.Element
55146      */
55147     getHeaderPanel : function(doShow){
55148         if(doShow){
55149             this.headerPanel.show();
55150         }
55151         return this.headerPanel;
55152     },
55153
55154     /**
55155      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55156      * After modifying the contents of this panel a call to grid.autoSize() may be
55157      * required to register any changes in size.
55158      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55159      * @return Roo.Element
55160      */
55161     getFooterPanel : function(doShow){
55162         if(doShow){
55163             this.footerPanel.show();
55164         }
55165         return this.footerPanel;
55166     },
55167
55168     initElements : function(){
55169         var E = Roo.Element;
55170         var el = this.grid.getGridEl().dom.firstChild;
55171         var cs = el.childNodes;
55172
55173         this.el = new E(el);
55174         
55175          this.focusEl = new E(el.firstChild);
55176         this.focusEl.swallowEvent("click", true);
55177         
55178         this.headerPanel = new E(cs[1]);
55179         this.headerPanel.enableDisplayMode("block");
55180
55181         this.scroller = new E(cs[2]);
55182         this.scrollSizer = new E(this.scroller.dom.firstChild);
55183
55184         this.lockedWrap = new E(cs[3]);
55185         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55186         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55187
55188         this.mainWrap = new E(cs[4]);
55189         this.mainHd = new E(this.mainWrap.dom.firstChild);
55190         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55191
55192         this.footerPanel = new E(cs[5]);
55193         this.footerPanel.enableDisplayMode("block");
55194
55195         this.resizeProxy = new E(cs[6]);
55196
55197         this.headerSelector = String.format(
55198            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55199            this.lockedHd.id, this.mainHd.id
55200         );
55201
55202         this.splitterSelector = String.format(
55203            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55204            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55205         );
55206     },
55207     idToCssName : function(s)
55208     {
55209         return s.replace(/[^a-z0-9]+/ig, '-');
55210     },
55211
55212     getHeaderCell : function(index){
55213         return Roo.DomQuery.select(this.headerSelector)[index];
55214     },
55215
55216     getHeaderCellMeasure : function(index){
55217         return this.getHeaderCell(index).firstChild;
55218     },
55219
55220     getHeaderCellText : function(index){
55221         return this.getHeaderCell(index).firstChild.firstChild;
55222     },
55223
55224     getLockedTable : function(){
55225         return this.lockedBody.dom.firstChild;
55226     },
55227
55228     getBodyTable : function(){
55229         return this.mainBody.dom.firstChild;
55230     },
55231
55232     getLockedRow : function(index){
55233         return this.getLockedTable().rows[index];
55234     },
55235
55236     getRow : function(index){
55237         return this.getBodyTable().rows[index];
55238     },
55239
55240     getRowComposite : function(index){
55241         if(!this.rowEl){
55242             this.rowEl = new Roo.CompositeElementLite();
55243         }
55244         var els = [], lrow, mrow;
55245         if(lrow = this.getLockedRow(index)){
55246             els.push(lrow);
55247         }
55248         if(mrow = this.getRow(index)){
55249             els.push(mrow);
55250         }
55251         this.rowEl.elements = els;
55252         return this.rowEl;
55253     },
55254     /**
55255      * Gets the 'td' of the cell
55256      * 
55257      * @param {Integer} rowIndex row to select
55258      * @param {Integer} colIndex column to select
55259      * 
55260      * @return {Object} 
55261      */
55262     getCell : function(rowIndex, colIndex){
55263         var locked = this.cm.getLockedCount();
55264         var source;
55265         if(colIndex < locked){
55266             source = this.lockedBody.dom.firstChild;
55267         }else{
55268             source = this.mainBody.dom.firstChild;
55269             colIndex -= locked;
55270         }
55271         return source.rows[rowIndex].childNodes[colIndex];
55272     },
55273
55274     getCellText : function(rowIndex, colIndex){
55275         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55276     },
55277
55278     getCellBox : function(cell){
55279         var b = this.fly(cell).getBox();
55280         if(Roo.isOpera){ // opera fails to report the Y
55281             b.y = cell.offsetTop + this.mainBody.getY();
55282         }
55283         return b;
55284     },
55285
55286     getCellIndex : function(cell){
55287         var id = String(cell.className).match(this.cellRE);
55288         if(id){
55289             return parseInt(id[1], 10);
55290         }
55291         return 0;
55292     },
55293
55294     findHeaderIndex : function(n){
55295         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55296         return r ? this.getCellIndex(r) : false;
55297     },
55298
55299     findHeaderCell : function(n){
55300         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55301         return r ? r : false;
55302     },
55303
55304     findRowIndex : function(n){
55305         if(!n){
55306             return false;
55307         }
55308         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55309         return r ? r.rowIndex : false;
55310     },
55311
55312     findCellIndex : function(node){
55313         var stop = this.el.dom;
55314         while(node && node != stop){
55315             if(this.findRE.test(node.className)){
55316                 return this.getCellIndex(node);
55317             }
55318             node = node.parentNode;
55319         }
55320         return false;
55321     },
55322
55323     getColumnId : function(index){
55324         return this.cm.getColumnId(index);
55325     },
55326
55327     getSplitters : function()
55328     {
55329         if(this.splitterSelector){
55330            return Roo.DomQuery.select(this.splitterSelector);
55331         }else{
55332             return null;
55333       }
55334     },
55335
55336     getSplitter : function(index){
55337         return this.getSplitters()[index];
55338     },
55339
55340     onRowOver : function(e, t){
55341         var row;
55342         if((row = this.findRowIndex(t)) !== false){
55343             this.getRowComposite(row).addClass("x-grid-row-over");
55344         }
55345     },
55346
55347     onRowOut : function(e, t){
55348         var row;
55349         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55350             this.getRowComposite(row).removeClass("x-grid-row-over");
55351         }
55352     },
55353
55354     renderHeaders : function(){
55355         var cm = this.cm;
55356         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55357         var cb = [], lb = [], sb = [], lsb = [], p = {};
55358         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55359             p.cellId = "x-grid-hd-0-" + i;
55360             p.splitId = "x-grid-csplit-0-" + i;
55361             p.id = cm.getColumnId(i);
55362             p.value = cm.getColumnHeader(i) || "";
55363             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55364             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55365             if(!cm.isLocked(i)){
55366                 cb[cb.length] = ct.apply(p);
55367                 sb[sb.length] = st.apply(p);
55368             }else{
55369                 lb[lb.length] = ct.apply(p);
55370                 lsb[lsb.length] = st.apply(p);
55371             }
55372         }
55373         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55374                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55375     },
55376
55377     updateHeaders : function(){
55378         var html = this.renderHeaders();
55379         this.lockedHd.update(html[0]);
55380         this.mainHd.update(html[1]);
55381     },
55382
55383     /**
55384      * Focuses the specified row.
55385      * @param {Number} row The row index
55386      */
55387     focusRow : function(row)
55388     {
55389         //Roo.log('GridView.focusRow');
55390         var x = this.scroller.dom.scrollLeft;
55391         this.focusCell(row, 0, false);
55392         this.scroller.dom.scrollLeft = x;
55393     },
55394
55395     /**
55396      * Focuses the specified cell.
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     focusCell : function(row, col, hscroll)
55402     {
55403         //Roo.log('GridView.focusCell');
55404         var el = this.ensureVisible(row, col, hscroll);
55405         this.focusEl.alignTo(el, "tl-tl");
55406         if(Roo.isGecko){
55407             this.focusEl.focus();
55408         }else{
55409             this.focusEl.focus.defer(1, this.focusEl);
55410         }
55411     },
55412
55413     /**
55414      * Scrolls the specified cell into view
55415      * @param {Number} row The row index
55416      * @param {Number} col The column index
55417      * @param {Boolean} hscroll false to disable horizontal scrolling
55418      */
55419     ensureVisible : function(row, col, hscroll)
55420     {
55421         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55422         //return null; //disable for testing.
55423         if(typeof row != "number"){
55424             row = row.rowIndex;
55425         }
55426         if(row < 0 && row >= this.ds.getCount()){
55427             return  null;
55428         }
55429         col = (col !== undefined ? col : 0);
55430         var cm = this.grid.colModel;
55431         while(cm.isHidden(col)){
55432             col++;
55433         }
55434
55435         var el = this.getCell(row, col);
55436         if(!el){
55437             return null;
55438         }
55439         var c = this.scroller.dom;
55440
55441         var ctop = parseInt(el.offsetTop, 10);
55442         var cleft = parseInt(el.offsetLeft, 10);
55443         var cbot = ctop + el.offsetHeight;
55444         var cright = cleft + el.offsetWidth;
55445         
55446         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55447         var stop = parseInt(c.scrollTop, 10);
55448         var sleft = parseInt(c.scrollLeft, 10);
55449         var sbot = stop + ch;
55450         var sright = sleft + c.clientWidth;
55451         /*
55452         Roo.log('GridView.ensureVisible:' +
55453                 ' ctop:' + ctop +
55454                 ' c.clientHeight:' + c.clientHeight +
55455                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55456                 ' stop:' + stop +
55457                 ' cbot:' + cbot +
55458                 ' sbot:' + sbot +
55459                 ' ch:' + ch  
55460                 );
55461         */
55462         if(ctop < stop){
55463              c.scrollTop = ctop;
55464             //Roo.log("set scrolltop to ctop DISABLE?");
55465         }else if(cbot > sbot){
55466             //Roo.log("set scrolltop to cbot-ch");
55467             c.scrollTop = cbot-ch;
55468         }
55469         
55470         if(hscroll !== false){
55471             if(cleft < sleft){
55472                 c.scrollLeft = cleft;
55473             }else if(cright > sright){
55474                 c.scrollLeft = cright-c.clientWidth;
55475             }
55476         }
55477          
55478         return el;
55479     },
55480
55481     updateColumns : function(){
55482         this.grid.stopEditing();
55483         var cm = this.grid.colModel, colIds = this.getColumnIds();
55484         //var totalWidth = cm.getTotalWidth();
55485         var pos = 0;
55486         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55487             //if(cm.isHidden(i)) continue;
55488             var w = cm.getColumnWidth(i);
55489             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55490             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55491         }
55492         this.updateSplitters();
55493     },
55494
55495     generateRules : function(cm){
55496         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55497         Roo.util.CSS.removeStyleSheet(rulesId);
55498         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55499             var cid = cm.getColumnId(i);
55500             var align = '';
55501             if(cm.config[i].align){
55502                 align = 'text-align:'+cm.config[i].align+';';
55503             }
55504             var hidden = '';
55505             if(cm.isHidden(i)){
55506                 hidden = 'display:none;';
55507             }
55508             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55509             ruleBuf.push(
55510                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55511                     this.hdSelector, cid, " {\n", align, width, "}\n",
55512                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55513                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55514         }
55515         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55516     },
55517
55518     updateSplitters : function(){
55519         var cm = this.cm, s = this.getSplitters();
55520         if(s){ // splitters not created yet
55521             var pos = 0, locked = true;
55522             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55523                 if(cm.isHidden(i)) {
55524                     continue;
55525                 }
55526                 var w = cm.getColumnWidth(i); // make sure it's a number
55527                 if(!cm.isLocked(i) && locked){
55528                     pos = 0;
55529                     locked = false;
55530                 }
55531                 pos += w;
55532                 s[i].style.left = (pos-this.splitOffset) + "px";
55533             }
55534         }
55535     },
55536
55537     handleHiddenChange : function(colModel, colIndex, hidden){
55538         if(hidden){
55539             this.hideColumn(colIndex);
55540         }else{
55541             this.unhideColumn(colIndex);
55542         }
55543     },
55544
55545     hideColumn : function(colIndex){
55546         var cid = this.getColumnId(colIndex);
55547         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55548         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55549         if(Roo.isSafari){
55550             this.updateHeaders();
55551         }
55552         this.updateSplitters();
55553         this.layout();
55554     },
55555
55556     unhideColumn : function(colIndex){
55557         var cid = this.getColumnId(colIndex);
55558         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55559         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55560
55561         if(Roo.isSafari){
55562             this.updateHeaders();
55563         }
55564         this.updateSplitters();
55565         this.layout();
55566     },
55567
55568     insertRows : function(dm, firstRow, lastRow, isUpdate){
55569         if(firstRow == 0 && lastRow == dm.getCount()-1){
55570             this.refresh();
55571         }else{
55572             if(!isUpdate){
55573                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55574             }
55575             var s = this.getScrollState();
55576             var markup = this.renderRows(firstRow, lastRow);
55577             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55578             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55579             this.restoreScroll(s);
55580             if(!isUpdate){
55581                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55582                 this.syncRowHeights(firstRow, lastRow);
55583                 this.stripeRows(firstRow);
55584                 this.layout();
55585             }
55586         }
55587     },
55588
55589     bufferRows : function(markup, target, index){
55590         var before = null, trows = target.rows, tbody = target.tBodies[0];
55591         if(index < trows.length){
55592             before = trows[index];
55593         }
55594         var b = document.createElement("div");
55595         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55596         var rows = b.firstChild.rows;
55597         for(var i = 0, len = rows.length; i < len; i++){
55598             if(before){
55599                 tbody.insertBefore(rows[0], before);
55600             }else{
55601                 tbody.appendChild(rows[0]);
55602             }
55603         }
55604         b.innerHTML = "";
55605         b = null;
55606     },
55607
55608     deleteRows : function(dm, firstRow, lastRow){
55609         if(dm.getRowCount()<1){
55610             this.fireEvent("beforerefresh", this);
55611             this.mainBody.update("");
55612             this.lockedBody.update("");
55613             this.fireEvent("refresh", this);
55614         }else{
55615             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55616             var bt = this.getBodyTable();
55617             var tbody = bt.firstChild;
55618             var rows = bt.rows;
55619             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55620                 tbody.removeChild(rows[firstRow]);
55621             }
55622             this.stripeRows(firstRow);
55623             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55624         }
55625     },
55626
55627     updateRows : function(dataSource, firstRow, lastRow){
55628         var s = this.getScrollState();
55629         this.refresh();
55630         this.restoreScroll(s);
55631     },
55632
55633     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55634         if(!noRefresh){
55635            this.refresh();
55636         }
55637         this.updateHeaderSortState();
55638     },
55639
55640     getScrollState : function(){
55641         
55642         var sb = this.scroller.dom;
55643         return {left: sb.scrollLeft, top: sb.scrollTop};
55644     },
55645
55646     stripeRows : function(startRow){
55647         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55648             return;
55649         }
55650         startRow = startRow || 0;
55651         var rows = this.getBodyTable().rows;
55652         var lrows = this.getLockedTable().rows;
55653         var cls = ' x-grid-row-alt ';
55654         for(var i = startRow, len = rows.length; i < len; i++){
55655             var row = rows[i], lrow = lrows[i];
55656             var isAlt = ((i+1) % 2 == 0);
55657             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55658             if(isAlt == hasAlt){
55659                 continue;
55660             }
55661             if(isAlt){
55662                 row.className += " x-grid-row-alt";
55663             }else{
55664                 row.className = row.className.replace("x-grid-row-alt", "");
55665             }
55666             if(lrow){
55667                 lrow.className = row.className;
55668             }
55669         }
55670     },
55671
55672     restoreScroll : function(state){
55673         //Roo.log('GridView.restoreScroll');
55674         var sb = this.scroller.dom;
55675         sb.scrollLeft = state.left;
55676         sb.scrollTop = state.top;
55677         this.syncScroll();
55678     },
55679
55680     syncScroll : function(){
55681         //Roo.log('GridView.syncScroll');
55682         var sb = this.scroller.dom;
55683         var sh = this.mainHd.dom;
55684         var bs = this.mainBody.dom;
55685         var lv = this.lockedBody.dom;
55686         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55687         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55688     },
55689
55690     handleScroll : function(e){
55691         this.syncScroll();
55692         var sb = this.scroller.dom;
55693         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55694         e.stopEvent();
55695     },
55696
55697     handleWheel : function(e){
55698         var d = e.getWheelDelta();
55699         this.scroller.dom.scrollTop -= d*22;
55700         // set this here to prevent jumpy scrolling on large tables
55701         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55702         e.stopEvent();
55703     },
55704
55705     renderRows : function(startRow, endRow){
55706         // pull in all the crap needed to render rows
55707         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55708         var colCount = cm.getColumnCount();
55709
55710         if(ds.getCount() < 1){
55711             return ["", ""];
55712         }
55713
55714         // build a map for all the columns
55715         var cs = [];
55716         for(var i = 0; i < colCount; i++){
55717             var name = cm.getDataIndex(i);
55718             cs[i] = {
55719                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55720                 renderer : cm.getRenderer(i),
55721                 id : cm.getColumnId(i),
55722                 locked : cm.isLocked(i),
55723                 has_editor : cm.isCellEditable(i)
55724             };
55725         }
55726
55727         startRow = startRow || 0;
55728         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55729
55730         // records to render
55731         var rs = ds.getRange(startRow, endRow);
55732
55733         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55734     },
55735
55736     // As much as I hate to duplicate code, this was branched because FireFox really hates
55737     // [].join("") on strings. The performance difference was substantial enough to
55738     // branch this function
55739     doRender : Roo.isGecko ?
55740             function(cs, rs, ds, startRow, colCount, stripe){
55741                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55742                 // buffers
55743                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55744                 
55745                 var hasListener = this.grid.hasListener('rowclass');
55746                 var rowcfg = {};
55747                 for(var j = 0, len = rs.length; j < len; j++){
55748                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55749                     for(var i = 0; i < colCount; i++){
55750                         c = cs[i];
55751                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55752                         p.id = c.id;
55753                         p.css = p.attr = "";
55754                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55755                         if(p.value == undefined || p.value === "") {
55756                             p.value = "&#160;";
55757                         }
55758                         if(c.has_editor){
55759                             p.css += ' x-grid-editable-cell';
55760                         }
55761                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55762                             p.css +=  ' x-grid-dirty-cell';
55763                         }
55764                         var markup = ct.apply(p);
55765                         if(!c.locked){
55766                             cb+= markup;
55767                         }else{
55768                             lcb+= markup;
55769                         }
55770                     }
55771                     var alt = [];
55772                     if(stripe && ((rowIndex+1) % 2 == 0)){
55773                         alt.push("x-grid-row-alt")
55774                     }
55775                     if(r.dirty){
55776                         alt.push(  " x-grid-dirty-row");
55777                     }
55778                     rp.cells = lcb;
55779                     if(this.getRowClass){
55780                         alt.push(this.getRowClass(r, rowIndex));
55781                     }
55782                     if (hasListener) {
55783                         rowcfg = {
55784                              
55785                             record: r,
55786                             rowIndex : rowIndex,
55787                             rowClass : ''
55788                         };
55789                         this.grid.fireEvent('rowclass', this, rowcfg);
55790                         alt.push(rowcfg.rowClass);
55791                     }
55792                     rp.alt = alt.join(" ");
55793                     lbuf+= rt.apply(rp);
55794                     rp.cells = cb;
55795                     buf+=  rt.apply(rp);
55796                 }
55797                 return [lbuf, buf];
55798             } :
55799             function(cs, rs, ds, startRow, colCount, stripe){
55800                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55801                 // buffers
55802                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55803                 var hasListener = this.grid.hasListener('rowclass');
55804  
55805                 var rowcfg = {};
55806                 for(var j = 0, len = rs.length; j < len; j++){
55807                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55808                     for(var i = 0; i < colCount; i++){
55809                         c = cs[i];
55810                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55811                         p.id = c.id;
55812                         p.css = p.attr = "";
55813                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55814                         if(p.value == undefined || p.value === "") {
55815                             p.value = "&#160;";
55816                         }
55817                         //Roo.log(c);
55818                          if(c.has_editor){
55819                             p.css += ' x-grid-editable-cell';
55820                         }
55821                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55822                             p.css += ' x-grid-dirty-cell' 
55823                         }
55824                         
55825                         var markup = ct.apply(p);
55826                         if(!c.locked){
55827                             cb[cb.length] = markup;
55828                         }else{
55829                             lcb[lcb.length] = markup;
55830                         }
55831                     }
55832                     var alt = [];
55833                     if(stripe && ((rowIndex+1) % 2 == 0)){
55834                         alt.push( "x-grid-row-alt");
55835                     }
55836                     if(r.dirty){
55837                         alt.push(" x-grid-dirty-row");
55838                     }
55839                     rp.cells = lcb;
55840                     if(this.getRowClass){
55841                         alt.push( this.getRowClass(r, rowIndex));
55842                     }
55843                     if (hasListener) {
55844                         rowcfg = {
55845                              
55846                             record: r,
55847                             rowIndex : rowIndex,
55848                             rowClass : ''
55849                         };
55850                         this.grid.fireEvent('rowclass', this, rowcfg);
55851                         alt.push(rowcfg.rowClass);
55852                     }
55853                     
55854                     rp.alt = alt.join(" ");
55855                     rp.cells = lcb.join("");
55856                     lbuf[lbuf.length] = rt.apply(rp);
55857                     rp.cells = cb.join("");
55858                     buf[buf.length] =  rt.apply(rp);
55859                 }
55860                 return [lbuf.join(""), buf.join("")];
55861             },
55862
55863     renderBody : function(){
55864         var markup = this.renderRows();
55865         var bt = this.templates.body;
55866         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
55867     },
55868
55869     /**
55870      * Refreshes the grid
55871      * @param {Boolean} headersToo
55872      */
55873     refresh : function(headersToo){
55874         this.fireEvent("beforerefresh", this);
55875         this.grid.stopEditing();
55876         var result = this.renderBody();
55877         this.lockedBody.update(result[0]);
55878         this.mainBody.update(result[1]);
55879         if(headersToo === true){
55880             this.updateHeaders();
55881             this.updateColumns();
55882             this.updateSplitters();
55883             this.updateHeaderSortState();
55884         }
55885         this.syncRowHeights();
55886         this.layout();
55887         this.fireEvent("refresh", this);
55888     },
55889
55890     handleColumnMove : function(cm, oldIndex, newIndex){
55891         this.indexMap = null;
55892         var s = this.getScrollState();
55893         this.refresh(true);
55894         this.restoreScroll(s);
55895         this.afterMove(newIndex);
55896     },
55897
55898     afterMove : function(colIndex){
55899         if(this.enableMoveAnim && Roo.enableFx){
55900             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
55901         }
55902         // if multisort - fix sortOrder, and reload..
55903         if (this.grid.dataSource.multiSort) {
55904             // the we can call sort again..
55905             var dm = this.grid.dataSource;
55906             var cm = this.grid.colModel;
55907             var so = [];
55908             for(var i = 0; i < cm.config.length; i++ ) {
55909                 
55910                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
55911                     continue; // dont' bother, it's not in sort list or being set.
55912                 }
55913                 
55914                 so.push(cm.config[i].dataIndex);
55915             };
55916             dm.sortOrder = so;
55917             dm.load(dm.lastOptions);
55918             
55919             
55920         }
55921         
55922     },
55923
55924     updateCell : function(dm, rowIndex, dataIndex){
55925         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
55926         if(typeof colIndex == "undefined"){ // not present in grid
55927             return;
55928         }
55929         var cm = this.grid.colModel;
55930         var cell = this.getCell(rowIndex, colIndex);
55931         var cellText = this.getCellText(rowIndex, colIndex);
55932
55933         var p = {
55934             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
55935             id : cm.getColumnId(colIndex),
55936             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
55937         };
55938         var renderer = cm.getRenderer(colIndex);
55939         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
55940         if(typeof val == "undefined" || val === "") {
55941             val = "&#160;";
55942         }
55943         cellText.innerHTML = val;
55944         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
55945         this.syncRowHeights(rowIndex, rowIndex);
55946     },
55947
55948     calcColumnWidth : function(colIndex, maxRowsToMeasure){
55949         var maxWidth = 0;
55950         if(this.grid.autoSizeHeaders){
55951             var h = this.getHeaderCellMeasure(colIndex);
55952             maxWidth = Math.max(maxWidth, h.scrollWidth);
55953         }
55954         var tb, index;
55955         if(this.cm.isLocked(colIndex)){
55956             tb = this.getLockedTable();
55957             index = colIndex;
55958         }else{
55959             tb = this.getBodyTable();
55960             index = colIndex - this.cm.getLockedCount();
55961         }
55962         if(tb && tb.rows){
55963             var rows = tb.rows;
55964             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
55965             for(var i = 0; i < stopIndex; i++){
55966                 var cell = rows[i].childNodes[index].firstChild;
55967                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
55968             }
55969         }
55970         return maxWidth + /*margin for error in IE*/ 5;
55971     },
55972     /**
55973      * Autofit a column to its content.
55974      * @param {Number} colIndex
55975      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
55976      */
55977      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
55978          if(this.cm.isHidden(colIndex)){
55979              return; // can't calc a hidden column
55980          }
55981         if(forceMinSize){
55982             var cid = this.cm.getColumnId(colIndex);
55983             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
55984            if(this.grid.autoSizeHeaders){
55985                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
55986            }
55987         }
55988         var newWidth = this.calcColumnWidth(colIndex);
55989         this.cm.setColumnWidth(colIndex,
55990             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
55991         if(!suppressEvent){
55992             this.grid.fireEvent("columnresize", colIndex, newWidth);
55993         }
55994     },
55995
55996     /**
55997      * Autofits all columns to their content and then expands to fit any extra space in the grid
55998      */
55999      autoSizeColumns : function(){
56000         var cm = this.grid.colModel;
56001         var colCount = cm.getColumnCount();
56002         for(var i = 0; i < colCount; i++){
56003             this.autoSizeColumn(i, true, true);
56004         }
56005         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56006             this.fitColumns();
56007         }else{
56008             this.updateColumns();
56009             this.layout();
56010         }
56011     },
56012
56013     /**
56014      * Autofits all columns to the grid's width proportionate with their current size
56015      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56016      */
56017     fitColumns : function(reserveScrollSpace){
56018         var cm = this.grid.colModel;
56019         var colCount = cm.getColumnCount();
56020         var cols = [];
56021         var width = 0;
56022         var i, w;
56023         for (i = 0; i < colCount; i++){
56024             if(!cm.isHidden(i) && !cm.isFixed(i)){
56025                 w = cm.getColumnWidth(i);
56026                 cols.push(i);
56027                 cols.push(w);
56028                 width += w;
56029             }
56030         }
56031         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56032         if(reserveScrollSpace){
56033             avail -= 17;
56034         }
56035         var frac = (avail - cm.getTotalWidth())/width;
56036         while (cols.length){
56037             w = cols.pop();
56038             i = cols.pop();
56039             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56040         }
56041         this.updateColumns();
56042         this.layout();
56043     },
56044
56045     onRowSelect : function(rowIndex){
56046         var row = this.getRowComposite(rowIndex);
56047         row.addClass("x-grid-row-selected");
56048     },
56049
56050     onRowDeselect : function(rowIndex){
56051         var row = this.getRowComposite(rowIndex);
56052         row.removeClass("x-grid-row-selected");
56053     },
56054
56055     onCellSelect : function(row, col){
56056         var cell = this.getCell(row, col);
56057         if(cell){
56058             Roo.fly(cell).addClass("x-grid-cell-selected");
56059         }
56060     },
56061
56062     onCellDeselect : function(row, col){
56063         var cell = this.getCell(row, col);
56064         if(cell){
56065             Roo.fly(cell).removeClass("x-grid-cell-selected");
56066         }
56067     },
56068
56069     updateHeaderSortState : function(){
56070         
56071         // sort state can be single { field: xxx, direction : yyy}
56072         // or   { xxx=>ASC , yyy : DESC ..... }
56073         
56074         var mstate = {};
56075         if (!this.ds.multiSort) { 
56076             var state = this.ds.getSortState();
56077             if(!state){
56078                 return;
56079             }
56080             mstate[state.field] = state.direction;
56081             // FIXME... - this is not used here.. but might be elsewhere..
56082             this.sortState = state;
56083             
56084         } else {
56085             mstate = this.ds.sortToggle;
56086         }
56087         //remove existing sort classes..
56088         
56089         var sc = this.sortClasses;
56090         var hds = this.el.select(this.headerSelector).removeClass(sc);
56091         
56092         for(var f in mstate) {
56093         
56094             var sortColumn = this.cm.findColumnIndex(f);
56095             
56096             if(sortColumn != -1){
56097                 var sortDir = mstate[f];        
56098                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56099             }
56100         }
56101         
56102          
56103         
56104     },
56105
56106
56107     handleHeaderClick : function(g, index,e){
56108         
56109         Roo.log("header click");
56110         
56111         if (Roo.isTouch) {
56112             // touch events on header are handled by context
56113             this.handleHdCtx(g,index,e);
56114             return;
56115         }
56116         
56117         
56118         if(this.headersDisabled){
56119             return;
56120         }
56121         var dm = g.dataSource, cm = g.colModel;
56122         if(!cm.isSortable(index)){
56123             return;
56124         }
56125         g.stopEditing();
56126         
56127         if (dm.multiSort) {
56128             // update the sortOrder
56129             var so = [];
56130             for(var i = 0; i < cm.config.length; i++ ) {
56131                 
56132                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56133                     continue; // dont' bother, it's not in sort list or being set.
56134                 }
56135                 
56136                 so.push(cm.config[i].dataIndex);
56137             };
56138             dm.sortOrder = so;
56139         }
56140         
56141         
56142         dm.sort(cm.getDataIndex(index));
56143     },
56144
56145
56146     destroy : function(){
56147         if(this.colMenu){
56148             this.colMenu.removeAll();
56149             Roo.menu.MenuMgr.unregister(this.colMenu);
56150             this.colMenu.getEl().remove();
56151             delete this.colMenu;
56152         }
56153         if(this.hmenu){
56154             this.hmenu.removeAll();
56155             Roo.menu.MenuMgr.unregister(this.hmenu);
56156             this.hmenu.getEl().remove();
56157             delete this.hmenu;
56158         }
56159         if(this.grid.enableColumnMove){
56160             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56161             if(dds){
56162                 for(var dd in dds){
56163                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56164                         var elid = dds[dd].dragElId;
56165                         dds[dd].unreg();
56166                         Roo.get(elid).remove();
56167                     } else if(dds[dd].config.isTarget){
56168                         dds[dd].proxyTop.remove();
56169                         dds[dd].proxyBottom.remove();
56170                         dds[dd].unreg();
56171                     }
56172                     if(Roo.dd.DDM.locationCache[dd]){
56173                         delete Roo.dd.DDM.locationCache[dd];
56174                     }
56175                 }
56176                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56177             }
56178         }
56179         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56180         this.bind(null, null);
56181         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56182     },
56183
56184     handleLockChange : function(){
56185         this.refresh(true);
56186     },
56187
56188     onDenyColumnLock : function(){
56189
56190     },
56191
56192     onDenyColumnHide : function(){
56193
56194     },
56195
56196     handleHdMenuClick : function(item){
56197         var index = this.hdCtxIndex;
56198         var cm = this.cm, ds = this.ds;
56199         switch(item.id){
56200             case "asc":
56201                 ds.sort(cm.getDataIndex(index), "ASC");
56202                 break;
56203             case "desc":
56204                 ds.sort(cm.getDataIndex(index), "DESC");
56205                 break;
56206             case "lock":
56207                 var lc = cm.getLockedCount();
56208                 if(cm.getColumnCount(true) <= lc+1){
56209                     this.onDenyColumnLock();
56210                     return;
56211                 }
56212                 if(lc != index){
56213                     cm.setLocked(index, true, true);
56214                     cm.moveColumn(index, lc);
56215                     this.grid.fireEvent("columnmove", index, lc);
56216                 }else{
56217                     cm.setLocked(index, true);
56218                 }
56219             break;
56220             case "unlock":
56221                 var lc = cm.getLockedCount();
56222                 if((lc-1) != index){
56223                     cm.setLocked(index, false, true);
56224                     cm.moveColumn(index, lc-1);
56225                     this.grid.fireEvent("columnmove", index, lc-1);
56226                 }else{
56227                     cm.setLocked(index, false);
56228                 }
56229             break;
56230             case 'wider': // used to expand cols on touch..
56231             case 'narrow':
56232                 var cw = cm.getColumnWidth(index);
56233                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56234                 cw = Math.max(0, cw);
56235                 cw = Math.min(cw,4000);
56236                 cm.setColumnWidth(index, cw);
56237                 break;
56238                 
56239             default:
56240                 index = cm.getIndexById(item.id.substr(4));
56241                 if(index != -1){
56242                     if(item.checked && cm.getColumnCount(true) <= 1){
56243                         this.onDenyColumnHide();
56244                         return false;
56245                     }
56246                     cm.setHidden(index, item.checked);
56247                 }
56248         }
56249         return true;
56250     },
56251
56252     beforeColMenuShow : function(){
56253         var cm = this.cm,  colCount = cm.getColumnCount();
56254         this.colMenu.removeAll();
56255         for(var i = 0; i < colCount; i++){
56256             this.colMenu.add(new Roo.menu.CheckItem({
56257                 id: "col-"+cm.getColumnId(i),
56258                 text: cm.getColumnHeader(i),
56259                 checked: !cm.isHidden(i),
56260                 hideOnClick:false
56261             }));
56262         }
56263     },
56264
56265     handleHdCtx : function(g, index, e){
56266         e.stopEvent();
56267         var hd = this.getHeaderCell(index);
56268         this.hdCtxIndex = index;
56269         var ms = this.hmenu.items, cm = this.cm;
56270         ms.get("asc").setDisabled(!cm.isSortable(index));
56271         ms.get("desc").setDisabled(!cm.isSortable(index));
56272         if(this.grid.enableColLock !== false){
56273             ms.get("lock").setDisabled(cm.isLocked(index));
56274             ms.get("unlock").setDisabled(!cm.isLocked(index));
56275         }
56276         this.hmenu.show(hd, "tl-bl");
56277     },
56278
56279     handleHdOver : function(e){
56280         var hd = this.findHeaderCell(e.getTarget());
56281         if(hd && !this.headersDisabled){
56282             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56283                this.fly(hd).addClass("x-grid-hd-over");
56284             }
56285         }
56286     },
56287
56288     handleHdOut : function(e){
56289         var hd = this.findHeaderCell(e.getTarget());
56290         if(hd){
56291             this.fly(hd).removeClass("x-grid-hd-over");
56292         }
56293     },
56294
56295     handleSplitDblClick : function(e, t){
56296         var i = this.getCellIndex(t);
56297         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56298             this.autoSizeColumn(i, true);
56299             this.layout();
56300         }
56301     },
56302
56303     render : function(){
56304
56305         var cm = this.cm;
56306         var colCount = cm.getColumnCount();
56307
56308         if(this.grid.monitorWindowResize === true){
56309             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56310         }
56311         var header = this.renderHeaders();
56312         var body = this.templates.body.apply({rows:""});
56313         var html = this.templates.master.apply({
56314             lockedBody: body,
56315             body: body,
56316             lockedHeader: header[0],
56317             header: header[1]
56318         });
56319
56320         //this.updateColumns();
56321
56322         this.grid.getGridEl().dom.innerHTML = html;
56323
56324         this.initElements();
56325         
56326         // a kludge to fix the random scolling effect in webkit
56327         this.el.on("scroll", function() {
56328             this.el.dom.scrollTop=0; // hopefully not recursive..
56329         },this);
56330
56331         this.scroller.on("scroll", this.handleScroll, this);
56332         this.lockedBody.on("mousewheel", this.handleWheel, this);
56333         this.mainBody.on("mousewheel", this.handleWheel, this);
56334
56335         this.mainHd.on("mouseover", this.handleHdOver, this);
56336         this.mainHd.on("mouseout", this.handleHdOut, this);
56337         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56338                 {delegate: "."+this.splitClass});
56339
56340         this.lockedHd.on("mouseover", this.handleHdOver, this);
56341         this.lockedHd.on("mouseout", this.handleHdOut, this);
56342         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56343                 {delegate: "."+this.splitClass});
56344
56345         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56346             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56347         }
56348
56349         this.updateSplitters();
56350
56351         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56352             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56353             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56354         }
56355
56356         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56357             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56358             this.hmenu.add(
56359                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56360                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56361             );
56362             if(this.grid.enableColLock !== false){
56363                 this.hmenu.add('-',
56364                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56365                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56366                 );
56367             }
56368             if (Roo.isTouch) {
56369                  this.hmenu.add('-',
56370                     {id:"wider", text: this.columnsWiderText},
56371                     {id:"narrow", text: this.columnsNarrowText }
56372                 );
56373                 
56374                  
56375             }
56376             
56377             if(this.grid.enableColumnHide !== false){
56378
56379                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56380                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56381                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56382
56383                 this.hmenu.add('-',
56384                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56385                 );
56386             }
56387             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56388
56389             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56390         }
56391
56392         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56393             this.dd = new Roo.grid.GridDragZone(this.grid, {
56394                 ddGroup : this.grid.ddGroup || 'GridDD'
56395             });
56396             
56397         }
56398
56399         /*
56400         for(var i = 0; i < colCount; i++){
56401             if(cm.isHidden(i)){
56402                 this.hideColumn(i);
56403             }
56404             if(cm.config[i].align){
56405                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56406                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56407             }
56408         }*/
56409         
56410         this.updateHeaderSortState();
56411
56412         this.beforeInitialResize();
56413         this.layout(true);
56414
56415         // two part rendering gives faster view to the user
56416         this.renderPhase2.defer(1, this);
56417     },
56418
56419     renderPhase2 : function(){
56420         // render the rows now
56421         this.refresh();
56422         if(this.grid.autoSizeColumns){
56423             this.autoSizeColumns();
56424         }
56425     },
56426
56427     beforeInitialResize : function(){
56428
56429     },
56430
56431     onColumnSplitterMoved : function(i, w){
56432         this.userResized = true;
56433         var cm = this.grid.colModel;
56434         cm.setColumnWidth(i, w, true);
56435         var cid = cm.getColumnId(i);
56436         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56437         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56438         this.updateSplitters();
56439         this.layout();
56440         this.grid.fireEvent("columnresize", i, w);
56441     },
56442
56443     syncRowHeights : function(startIndex, endIndex){
56444         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56445             startIndex = startIndex || 0;
56446             var mrows = this.getBodyTable().rows;
56447             var lrows = this.getLockedTable().rows;
56448             var len = mrows.length-1;
56449             endIndex = Math.min(endIndex || len, len);
56450             for(var i = startIndex; i <= endIndex; i++){
56451                 var m = mrows[i], l = lrows[i];
56452                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56453                 m.style.height = l.style.height = h + "px";
56454             }
56455         }
56456     },
56457
56458     layout : function(initialRender, is2ndPass){
56459         var g = this.grid;
56460         var auto = g.autoHeight;
56461         var scrollOffset = 16;
56462         var c = g.getGridEl(), cm = this.cm,
56463                 expandCol = g.autoExpandColumn,
56464                 gv = this;
56465         //c.beginMeasure();
56466
56467         if(!c.dom.offsetWidth){ // display:none?
56468             if(initialRender){
56469                 this.lockedWrap.show();
56470                 this.mainWrap.show();
56471             }
56472             return;
56473         }
56474
56475         var hasLock = this.cm.isLocked(0);
56476
56477         var tbh = this.headerPanel.getHeight();
56478         var bbh = this.footerPanel.getHeight();
56479
56480         if(auto){
56481             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56482             var newHeight = ch + c.getBorderWidth("tb");
56483             if(g.maxHeight){
56484                 newHeight = Math.min(g.maxHeight, newHeight);
56485             }
56486             c.setHeight(newHeight);
56487         }
56488
56489         if(g.autoWidth){
56490             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56491         }
56492
56493         var s = this.scroller;
56494
56495         var csize = c.getSize(true);
56496
56497         this.el.setSize(csize.width, csize.height);
56498
56499         this.headerPanel.setWidth(csize.width);
56500         this.footerPanel.setWidth(csize.width);
56501
56502         var hdHeight = this.mainHd.getHeight();
56503         var vw = csize.width;
56504         var vh = csize.height - (tbh + bbh);
56505
56506         s.setSize(vw, vh);
56507
56508         var bt = this.getBodyTable();
56509         
56510         if(cm.getLockedCount() == cm.config.length){
56511             bt = this.getLockedTable();
56512         }
56513         
56514         var ltWidth = hasLock ?
56515                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56516
56517         var scrollHeight = bt.offsetHeight;
56518         var scrollWidth = ltWidth + bt.offsetWidth;
56519         var vscroll = false, hscroll = false;
56520
56521         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56522
56523         var lw = this.lockedWrap, mw = this.mainWrap;
56524         var lb = this.lockedBody, mb = this.mainBody;
56525
56526         setTimeout(function(){
56527             var t = s.dom.offsetTop;
56528             var w = s.dom.clientWidth,
56529                 h = s.dom.clientHeight;
56530
56531             lw.setTop(t);
56532             lw.setSize(ltWidth, h);
56533
56534             mw.setLeftTop(ltWidth, t);
56535             mw.setSize(w-ltWidth, h);
56536
56537             lb.setHeight(h-hdHeight);
56538             mb.setHeight(h-hdHeight);
56539
56540             if(is2ndPass !== true && !gv.userResized && expandCol){
56541                 // high speed resize without full column calculation
56542                 
56543                 var ci = cm.getIndexById(expandCol);
56544                 if (ci < 0) {
56545                     ci = cm.findColumnIndex(expandCol);
56546                 }
56547                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56548                 var expandId = cm.getColumnId(ci);
56549                 var  tw = cm.getTotalWidth(false);
56550                 var currentWidth = cm.getColumnWidth(ci);
56551                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56552                 if(currentWidth != cw){
56553                     cm.setColumnWidth(ci, cw, true);
56554                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56555                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56556                     gv.updateSplitters();
56557                     gv.layout(false, true);
56558                 }
56559             }
56560
56561             if(initialRender){
56562                 lw.show();
56563                 mw.show();
56564             }
56565             //c.endMeasure();
56566         }, 10);
56567     },
56568
56569     onWindowResize : function(){
56570         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56571             return;
56572         }
56573         this.layout();
56574     },
56575
56576     appendFooter : function(parentEl){
56577         return null;
56578     },
56579
56580     sortAscText : "Sort Ascending",
56581     sortDescText : "Sort Descending",
56582     lockText : "Lock Column",
56583     unlockText : "Unlock Column",
56584     columnsText : "Columns",
56585  
56586     columnsWiderText : "Wider",
56587     columnsNarrowText : "Thinner"
56588 });
56589
56590
56591 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56592     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56593     this.proxy.el.addClass('x-grid3-col-dd');
56594 };
56595
56596 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56597     handleMouseDown : function(e){
56598
56599     },
56600
56601     callHandleMouseDown : function(e){
56602         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56603     }
56604 });
56605 /*
56606  * Based on:
56607  * Ext JS Library 1.1.1
56608  * Copyright(c) 2006-2007, Ext JS, LLC.
56609  *
56610  * Originally Released Under LGPL - original licence link has changed is not relivant.
56611  *
56612  * Fork - LGPL
56613  * <script type="text/javascript">
56614  */
56615  
56616 // private
56617 // This is a support class used internally by the Grid components
56618 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56619     this.grid = grid;
56620     this.view = grid.getView();
56621     this.proxy = this.view.resizeProxy;
56622     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56623         "gridSplitters" + this.grid.getGridEl().id, {
56624         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56625     });
56626     this.setHandleElId(Roo.id(hd));
56627     this.setOuterHandleElId(Roo.id(hd2));
56628     this.scroll = false;
56629 };
56630 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56631     fly: Roo.Element.fly,
56632
56633     b4StartDrag : function(x, y){
56634         this.view.headersDisabled = true;
56635         this.proxy.setHeight(this.view.mainWrap.getHeight());
56636         var w = this.cm.getColumnWidth(this.cellIndex);
56637         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56638         this.resetConstraints();
56639         this.setXConstraint(minw, 1000);
56640         this.setYConstraint(0, 0);
56641         this.minX = x - minw;
56642         this.maxX = x + 1000;
56643         this.startPos = x;
56644         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56645     },
56646
56647
56648     handleMouseDown : function(e){
56649         ev = Roo.EventObject.setEvent(e);
56650         var t = this.fly(ev.getTarget());
56651         if(t.hasClass("x-grid-split")){
56652             this.cellIndex = this.view.getCellIndex(t.dom);
56653             this.split = t.dom;
56654             this.cm = this.grid.colModel;
56655             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56656                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56657             }
56658         }
56659     },
56660
56661     endDrag : function(e){
56662         this.view.headersDisabled = false;
56663         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56664         var diff = endX - this.startPos;
56665         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56666     },
56667
56668     autoOffset : function(){
56669         this.setDelta(0,0);
56670     }
56671 });/*
56672  * Based on:
56673  * Ext JS Library 1.1.1
56674  * Copyright(c) 2006-2007, Ext JS, LLC.
56675  *
56676  * Originally Released Under LGPL - original licence link has changed is not relivant.
56677  *
56678  * Fork - LGPL
56679  * <script type="text/javascript">
56680  */
56681  
56682 // private
56683 // This is a support class used internally by the Grid components
56684 Roo.grid.GridDragZone = function(grid, config){
56685     this.view = grid.getView();
56686     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56687     if(this.view.lockedBody){
56688         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56689         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56690     }
56691     this.scroll = false;
56692     this.grid = grid;
56693     this.ddel = document.createElement('div');
56694     this.ddel.className = 'x-grid-dd-wrap';
56695 };
56696
56697 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56698     ddGroup : "GridDD",
56699
56700     getDragData : function(e){
56701         var t = Roo.lib.Event.getTarget(e);
56702         var rowIndex = this.view.findRowIndex(t);
56703         var sm = this.grid.selModel;
56704             
56705         //Roo.log(rowIndex);
56706         
56707         if (sm.getSelectedCell) {
56708             // cell selection..
56709             if (!sm.getSelectedCell()) {
56710                 return false;
56711             }
56712             if (rowIndex != sm.getSelectedCell()[0]) {
56713                 return false;
56714             }
56715         
56716         }
56717         
56718         if(rowIndex !== false){
56719             
56720             // if editorgrid.. 
56721             
56722             
56723             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56724                
56725             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56726               //  
56727             //}
56728             if (e.hasModifier()){
56729                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56730             }
56731             
56732             Roo.log("getDragData");
56733             
56734             return {
56735                 grid: this.grid,
56736                 ddel: this.ddel,
56737                 rowIndex: rowIndex,
56738                 selections:sm.getSelections ? sm.getSelections() : (
56739                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56740                 )
56741             };
56742         }
56743         return false;
56744     },
56745
56746     onInitDrag : function(e){
56747         var data = this.dragData;
56748         this.ddel.innerHTML = this.grid.getDragDropText();
56749         this.proxy.update(this.ddel);
56750         // fire start drag?
56751     },
56752
56753     afterRepair : function(){
56754         this.dragging = false;
56755     },
56756
56757     getRepairXY : function(e, data){
56758         return false;
56759     },
56760
56761     onEndDrag : function(data, e){
56762         // fire end drag?
56763     },
56764
56765     onValidDrop : function(dd, e, id){
56766         // fire drag drop?
56767         this.hideProxy();
56768     },
56769
56770     beforeInvalidDrop : function(e, id){
56771
56772     }
56773 });/*
56774  * Based on:
56775  * Ext JS Library 1.1.1
56776  * Copyright(c) 2006-2007, Ext JS, LLC.
56777  *
56778  * Originally Released Under LGPL - original licence link has changed is not relivant.
56779  *
56780  * Fork - LGPL
56781  * <script type="text/javascript">
56782  */
56783  
56784
56785 /**
56786  * @class Roo.grid.ColumnModel
56787  * @extends Roo.util.Observable
56788  * This is the default implementation of a ColumnModel used by the Grid. It defines
56789  * the columns in the grid.
56790  * <br>Usage:<br>
56791  <pre><code>
56792  var colModel = new Roo.grid.ColumnModel([
56793         {header: "Ticker", width: 60, sortable: true, locked: true},
56794         {header: "Company Name", width: 150, sortable: true},
56795         {header: "Market Cap.", width: 100, sortable: true},
56796         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56797         {header: "Employees", width: 100, sortable: true, resizable: false}
56798  ]);
56799  </code></pre>
56800  * <p>
56801  
56802  * The config options listed for this class are options which may appear in each
56803  * individual column definition.
56804  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56805  * @constructor
56806  * @param {Object} config An Array of column config objects. See this class's
56807  * config objects for details.
56808 */
56809 Roo.grid.ColumnModel = function(config){
56810         /**
56811      * The config passed into the constructor
56812      */
56813     this.config = config;
56814     this.lookup = {};
56815
56816     // if no id, create one
56817     // if the column does not have a dataIndex mapping,
56818     // map it to the order it is in the config
56819     for(var i = 0, len = config.length; i < len; i++){
56820         var c = config[i];
56821         if(typeof c.dataIndex == "undefined"){
56822             c.dataIndex = i;
56823         }
56824         if(typeof c.renderer == "string"){
56825             c.renderer = Roo.util.Format[c.renderer];
56826         }
56827         if(typeof c.id == "undefined"){
56828             c.id = Roo.id();
56829         }
56830         if(c.editor && c.editor.xtype){
56831             c.editor  = Roo.factory(c.editor, Roo.grid);
56832         }
56833         if(c.editor && c.editor.isFormField){
56834             c.editor = new Roo.grid.GridEditor(c.editor);
56835         }
56836         this.lookup[c.id] = c;
56837     }
56838
56839     /**
56840      * The width of columns which have no width specified (defaults to 100)
56841      * @type Number
56842      */
56843     this.defaultWidth = 100;
56844
56845     /**
56846      * Default sortable of columns which have no sortable specified (defaults to false)
56847      * @type Boolean
56848      */
56849     this.defaultSortable = false;
56850
56851     this.addEvents({
56852         /**
56853              * @event widthchange
56854              * Fires when the width of a column changes.
56855              * @param {ColumnModel} this
56856              * @param {Number} columnIndex The column index
56857              * @param {Number} newWidth The new width
56858              */
56859             "widthchange": true,
56860         /**
56861              * @event headerchange
56862              * Fires when the text of a header changes.
56863              * @param {ColumnModel} this
56864              * @param {Number} columnIndex The column index
56865              * @param {Number} newText The new header text
56866              */
56867             "headerchange": true,
56868         /**
56869              * @event hiddenchange
56870              * Fires when a column is hidden or "unhidden".
56871              * @param {ColumnModel} this
56872              * @param {Number} columnIndex The column index
56873              * @param {Boolean} hidden true if hidden, false otherwise
56874              */
56875             "hiddenchange": true,
56876             /**
56877          * @event columnmoved
56878          * Fires when a column is moved.
56879          * @param {ColumnModel} this
56880          * @param {Number} oldIndex
56881          * @param {Number} newIndex
56882          */
56883         "columnmoved" : true,
56884         /**
56885          * @event columlockchange
56886          * Fires when a column's locked state is changed
56887          * @param {ColumnModel} this
56888          * @param {Number} colIndex
56889          * @param {Boolean} locked true if locked
56890          */
56891         "columnlockchange" : true
56892     });
56893     Roo.grid.ColumnModel.superclass.constructor.call(this);
56894 };
56895 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
56896     /**
56897      * @cfg {String} header The header text to display in the Grid view.
56898      */
56899     /**
56900      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
56901      * {@link Roo.data.Record} definition from which to draw the column's value. If not
56902      * specified, the column's index is used as an index into the Record's data Array.
56903      */
56904     /**
56905      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
56906      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
56907      */
56908     /**
56909      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
56910      * Defaults to the value of the {@link #defaultSortable} property.
56911      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
56912      */
56913     /**
56914      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
56915      */
56916     /**
56917      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
56918      */
56919     /**
56920      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
56921      */
56922     /**
56923      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
56924      */
56925     /**
56926      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
56927      * given the cell's data value. See {@link #setRenderer}. If not specified, the
56928      * default renderer uses the raw data value. If an object is returned (bootstrap only)
56929      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
56930      */
56931        /**
56932      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
56933      */
56934     /**
56935      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
56936      */
56937     /**
56938      * @cfg {String} cursor (Optional)
56939      */
56940     /**
56941      * @cfg {String} tooltip (Optional)
56942      */
56943     /**
56944      * @cfg {Number} xs (Optional)
56945      */
56946     /**
56947      * @cfg {Number} sm (Optional)
56948      */
56949     /**
56950      * @cfg {Number} md (Optional)
56951      */
56952     /**
56953      * @cfg {Number} lg (Optional)
56954      */
56955     /**
56956      * Returns the id of the column at the specified index.
56957      * @param {Number} index The column index
56958      * @return {String} the id
56959      */
56960     getColumnId : function(index){
56961         return this.config[index].id;
56962     },
56963
56964     /**
56965      * Returns the column for a specified id.
56966      * @param {String} id The column id
56967      * @return {Object} the column
56968      */
56969     getColumnById : function(id){
56970         return this.lookup[id];
56971     },
56972
56973     
56974     /**
56975      * Returns the column for a specified dataIndex.
56976      * @param {String} dataIndex The column dataIndex
56977      * @return {Object|Boolean} the column or false if not found
56978      */
56979     getColumnByDataIndex: function(dataIndex){
56980         var index = this.findColumnIndex(dataIndex);
56981         return index > -1 ? this.config[index] : false;
56982     },
56983     
56984     /**
56985      * Returns the index for a specified column id.
56986      * @param {String} id The column id
56987      * @return {Number} the index, or -1 if not found
56988      */
56989     getIndexById : function(id){
56990         for(var i = 0, len = this.config.length; i < len; i++){
56991             if(this.config[i].id == id){
56992                 return i;
56993             }
56994         }
56995         return -1;
56996     },
56997     
56998     /**
56999      * Returns the index for a specified column dataIndex.
57000      * @param {String} dataIndex The column dataIndex
57001      * @return {Number} the index, or -1 if not found
57002      */
57003     
57004     findColumnIndex : function(dataIndex){
57005         for(var i = 0, len = this.config.length; i < len; i++){
57006             if(this.config[i].dataIndex == dataIndex){
57007                 return i;
57008             }
57009         }
57010         return -1;
57011     },
57012     
57013     
57014     moveColumn : function(oldIndex, newIndex){
57015         var c = this.config[oldIndex];
57016         this.config.splice(oldIndex, 1);
57017         this.config.splice(newIndex, 0, c);
57018         this.dataMap = null;
57019         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57020     },
57021
57022     isLocked : function(colIndex){
57023         return this.config[colIndex].locked === true;
57024     },
57025
57026     setLocked : function(colIndex, value, suppressEvent){
57027         if(this.isLocked(colIndex) == value){
57028             return;
57029         }
57030         this.config[colIndex].locked = value;
57031         if(!suppressEvent){
57032             this.fireEvent("columnlockchange", this, colIndex, value);
57033         }
57034     },
57035
57036     getTotalLockedWidth : function(){
57037         var totalWidth = 0;
57038         for(var i = 0; i < this.config.length; i++){
57039             if(this.isLocked(i) && !this.isHidden(i)){
57040                 this.totalWidth += this.getColumnWidth(i);
57041             }
57042         }
57043         return totalWidth;
57044     },
57045
57046     getLockedCount : function(){
57047         for(var i = 0, len = this.config.length; i < len; i++){
57048             if(!this.isLocked(i)){
57049                 return i;
57050             }
57051         }
57052         
57053         return this.config.length;
57054     },
57055
57056     /**
57057      * Returns the number of columns.
57058      * @return {Number}
57059      */
57060     getColumnCount : function(visibleOnly){
57061         if(visibleOnly === true){
57062             var c = 0;
57063             for(var i = 0, len = this.config.length; i < len; i++){
57064                 if(!this.isHidden(i)){
57065                     c++;
57066                 }
57067             }
57068             return c;
57069         }
57070         return this.config.length;
57071     },
57072
57073     /**
57074      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57075      * @param {Function} fn
57076      * @param {Object} scope (optional)
57077      * @return {Array} result
57078      */
57079     getColumnsBy : function(fn, scope){
57080         var r = [];
57081         for(var i = 0, len = this.config.length; i < len; i++){
57082             var c = this.config[i];
57083             if(fn.call(scope||this, c, i) === true){
57084                 r[r.length] = c;
57085             }
57086         }
57087         return r;
57088     },
57089
57090     /**
57091      * Returns true if the specified column is sortable.
57092      * @param {Number} col The column index
57093      * @return {Boolean}
57094      */
57095     isSortable : function(col){
57096         if(typeof this.config[col].sortable == "undefined"){
57097             return this.defaultSortable;
57098         }
57099         return this.config[col].sortable;
57100     },
57101
57102     /**
57103      * Returns the rendering (formatting) function defined for the column.
57104      * @param {Number} col The column index.
57105      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57106      */
57107     getRenderer : function(col){
57108         if(!this.config[col].renderer){
57109             return Roo.grid.ColumnModel.defaultRenderer;
57110         }
57111         return this.config[col].renderer;
57112     },
57113
57114     /**
57115      * Sets the rendering (formatting) function for a column.
57116      * @param {Number} col The column index
57117      * @param {Function} fn The function to use to process the cell's raw data
57118      * to return HTML markup for the grid view. The render function is called with
57119      * the following parameters:<ul>
57120      * <li>Data value.</li>
57121      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57122      * <li>css A CSS style string to apply to the table cell.</li>
57123      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57124      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57125      * <li>Row index</li>
57126      * <li>Column index</li>
57127      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57128      */
57129     setRenderer : function(col, fn){
57130         this.config[col].renderer = fn;
57131     },
57132
57133     /**
57134      * Returns the width for the specified column.
57135      * @param {Number} col The column index
57136      * @return {Number}
57137      */
57138     getColumnWidth : function(col){
57139         return this.config[col].width * 1 || this.defaultWidth;
57140     },
57141
57142     /**
57143      * Sets the width for a column.
57144      * @param {Number} col The column index
57145      * @param {Number} width The new width
57146      */
57147     setColumnWidth : function(col, width, suppressEvent){
57148         this.config[col].width = width;
57149         this.totalWidth = null;
57150         if(!suppressEvent){
57151              this.fireEvent("widthchange", this, col, width);
57152         }
57153     },
57154
57155     /**
57156      * Returns the total width of all columns.
57157      * @param {Boolean} includeHidden True to include hidden column widths
57158      * @return {Number}
57159      */
57160     getTotalWidth : function(includeHidden){
57161         if(!this.totalWidth){
57162             this.totalWidth = 0;
57163             for(var i = 0, len = this.config.length; i < len; i++){
57164                 if(includeHidden || !this.isHidden(i)){
57165                     this.totalWidth += this.getColumnWidth(i);
57166                 }
57167             }
57168         }
57169         return this.totalWidth;
57170     },
57171
57172     /**
57173      * Returns the header for the specified column.
57174      * @param {Number} col The column index
57175      * @return {String}
57176      */
57177     getColumnHeader : function(col){
57178         return this.config[col].header;
57179     },
57180
57181     /**
57182      * Sets the header for a column.
57183      * @param {Number} col The column index
57184      * @param {String} header The new header
57185      */
57186     setColumnHeader : function(col, header){
57187         this.config[col].header = header;
57188         this.fireEvent("headerchange", this, col, header);
57189     },
57190
57191     /**
57192      * Returns the tooltip for the specified column.
57193      * @param {Number} col The column index
57194      * @return {String}
57195      */
57196     getColumnTooltip : function(col){
57197             return this.config[col].tooltip;
57198     },
57199     /**
57200      * Sets the tooltip for a column.
57201      * @param {Number} col The column index
57202      * @param {String} tooltip The new tooltip
57203      */
57204     setColumnTooltip : function(col, tooltip){
57205             this.config[col].tooltip = tooltip;
57206     },
57207
57208     /**
57209      * Returns the dataIndex for the specified column.
57210      * @param {Number} col The column index
57211      * @return {Number}
57212      */
57213     getDataIndex : function(col){
57214         return this.config[col].dataIndex;
57215     },
57216
57217     /**
57218      * Sets the dataIndex for a column.
57219      * @param {Number} col The column index
57220      * @param {Number} dataIndex The new dataIndex
57221      */
57222     setDataIndex : function(col, dataIndex){
57223         this.config[col].dataIndex = dataIndex;
57224     },
57225
57226     
57227     
57228     /**
57229      * Returns true if the cell is editable.
57230      * @param {Number} colIndex The column index
57231      * @param {Number} rowIndex The row index - this is nto actually used..?
57232      * @return {Boolean}
57233      */
57234     isCellEditable : function(colIndex, rowIndex){
57235         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57236     },
57237
57238     /**
57239      * Returns the editor defined for the cell/column.
57240      * return false or null to disable editing.
57241      * @param {Number} colIndex The column index
57242      * @param {Number} rowIndex The row index
57243      * @return {Object}
57244      */
57245     getCellEditor : function(colIndex, rowIndex){
57246         return this.config[colIndex].editor;
57247     },
57248
57249     /**
57250      * Sets if a column is editable.
57251      * @param {Number} col The column index
57252      * @param {Boolean} editable True if the column is editable
57253      */
57254     setEditable : function(col, editable){
57255         this.config[col].editable = editable;
57256     },
57257
57258
57259     /**
57260      * Returns true if the column is hidden.
57261      * @param {Number} colIndex The column index
57262      * @return {Boolean}
57263      */
57264     isHidden : function(colIndex){
57265         return this.config[colIndex].hidden;
57266     },
57267
57268
57269     /**
57270      * Returns true if the column width cannot be changed
57271      */
57272     isFixed : function(colIndex){
57273         return this.config[colIndex].fixed;
57274     },
57275
57276     /**
57277      * Returns true if the column can be resized
57278      * @return {Boolean}
57279      */
57280     isResizable : function(colIndex){
57281         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57282     },
57283     /**
57284      * Sets if a column is hidden.
57285      * @param {Number} colIndex The column index
57286      * @param {Boolean} hidden True if the column is hidden
57287      */
57288     setHidden : function(colIndex, hidden){
57289         this.config[colIndex].hidden = hidden;
57290         this.totalWidth = null;
57291         this.fireEvent("hiddenchange", this, colIndex, hidden);
57292     },
57293
57294     /**
57295      * Sets the editor for a column.
57296      * @param {Number} col The column index
57297      * @param {Object} editor The editor object
57298      */
57299     setEditor : function(col, editor){
57300         this.config[col].editor = editor;
57301     }
57302 });
57303
57304 Roo.grid.ColumnModel.defaultRenderer = function(value){
57305         if(typeof value == "string" && value.length < 1){
57306             return "&#160;";
57307         }
57308         return value;
57309 };
57310
57311 // Alias for backwards compatibility
57312 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57313 /*
57314  * Based on:
57315  * Ext JS Library 1.1.1
57316  * Copyright(c) 2006-2007, Ext JS, LLC.
57317  *
57318  * Originally Released Under LGPL - original licence link has changed is not relivant.
57319  *
57320  * Fork - LGPL
57321  * <script type="text/javascript">
57322  */
57323
57324 /**
57325  * @class Roo.grid.AbstractSelectionModel
57326  * @extends Roo.util.Observable
57327  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57328  * implemented by descendant classes.  This class should not be directly instantiated.
57329  * @constructor
57330  */
57331 Roo.grid.AbstractSelectionModel = function(){
57332     this.locked = false;
57333     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57334 };
57335
57336 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57337     /** @ignore Called by the grid automatically. Do not call directly. */
57338     init : function(grid){
57339         this.grid = grid;
57340         this.initEvents();
57341     },
57342
57343     /**
57344      * Locks the selections.
57345      */
57346     lock : function(){
57347         this.locked = true;
57348     },
57349
57350     /**
57351      * Unlocks the selections.
57352      */
57353     unlock : function(){
57354         this.locked = false;
57355     },
57356
57357     /**
57358      * Returns true if the selections are locked.
57359      * @return {Boolean}
57360      */
57361     isLocked : function(){
57362         return this.locked;
57363     }
57364 });/*
57365  * Based on:
57366  * Ext JS Library 1.1.1
57367  * Copyright(c) 2006-2007, Ext JS, LLC.
57368  *
57369  * Originally Released Under LGPL - original licence link has changed is not relivant.
57370  *
57371  * Fork - LGPL
57372  * <script type="text/javascript">
57373  */
57374 /**
57375  * @extends Roo.grid.AbstractSelectionModel
57376  * @class Roo.grid.RowSelectionModel
57377  * The default SelectionModel used by {@link Roo.grid.Grid}.
57378  * It supports multiple selections and keyboard selection/navigation. 
57379  * @constructor
57380  * @param {Object} config
57381  */
57382 Roo.grid.RowSelectionModel = function(config){
57383     Roo.apply(this, config);
57384     this.selections = new Roo.util.MixedCollection(false, function(o){
57385         return o.id;
57386     });
57387
57388     this.last = false;
57389     this.lastActive = false;
57390
57391     this.addEvents({
57392         /**
57393              * @event selectionchange
57394              * Fires when the selection changes
57395              * @param {SelectionModel} this
57396              */
57397             "selectionchange" : true,
57398         /**
57399              * @event afterselectionchange
57400              * Fires after the selection changes (eg. by key press or clicking)
57401              * @param {SelectionModel} this
57402              */
57403             "afterselectionchange" : true,
57404         /**
57405              * @event beforerowselect
57406              * Fires when a row is selected being selected, return false to cancel.
57407              * @param {SelectionModel} this
57408              * @param {Number} rowIndex The selected index
57409              * @param {Boolean} keepExisting False if other selections will be cleared
57410              */
57411             "beforerowselect" : true,
57412         /**
57413              * @event rowselect
57414              * Fires when a row is selected.
57415              * @param {SelectionModel} this
57416              * @param {Number} rowIndex The selected index
57417              * @param {Roo.data.Record} r The record
57418              */
57419             "rowselect" : true,
57420         /**
57421              * @event rowdeselect
57422              * Fires when a row is deselected.
57423              * @param {SelectionModel} this
57424              * @param {Number} rowIndex The selected index
57425              */
57426         "rowdeselect" : true
57427     });
57428     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57429     this.locked = false;
57430 };
57431
57432 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57433     /**
57434      * @cfg {Boolean} singleSelect
57435      * True to allow selection of only one row at a time (defaults to false)
57436      */
57437     singleSelect : false,
57438
57439     // private
57440     initEvents : function(){
57441
57442         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57443             this.grid.on("mousedown", this.handleMouseDown, this);
57444         }else{ // allow click to work like normal
57445             this.grid.on("rowclick", this.handleDragableRowClick, this);
57446         }
57447
57448         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57449             "up" : function(e){
57450                 if(!e.shiftKey){
57451                     this.selectPrevious(e.shiftKey);
57452                 }else if(this.last !== false && this.lastActive !== false){
57453                     var last = this.last;
57454                     this.selectRange(this.last,  this.lastActive-1);
57455                     this.grid.getView().focusRow(this.lastActive);
57456                     if(last !== false){
57457                         this.last = last;
57458                     }
57459                 }else{
57460                     this.selectFirstRow();
57461                 }
57462                 this.fireEvent("afterselectionchange", this);
57463             },
57464             "down" : function(e){
57465                 if(!e.shiftKey){
57466                     this.selectNext(e.shiftKey);
57467                 }else if(this.last !== false && this.lastActive !== false){
57468                     var last = this.last;
57469                     this.selectRange(this.last,  this.lastActive+1);
57470                     this.grid.getView().focusRow(this.lastActive);
57471                     if(last !== false){
57472                         this.last = last;
57473                     }
57474                 }else{
57475                     this.selectFirstRow();
57476                 }
57477                 this.fireEvent("afterselectionchange", this);
57478             },
57479             scope: this
57480         });
57481
57482         var view = this.grid.view;
57483         view.on("refresh", this.onRefresh, this);
57484         view.on("rowupdated", this.onRowUpdated, this);
57485         view.on("rowremoved", this.onRemove, this);
57486     },
57487
57488     // private
57489     onRefresh : function(){
57490         var ds = this.grid.dataSource, i, v = this.grid.view;
57491         var s = this.selections;
57492         s.each(function(r){
57493             if((i = ds.indexOfId(r.id)) != -1){
57494                 v.onRowSelect(i);
57495                 s.add(ds.getAt(i)); // updating the selection relate data
57496             }else{
57497                 s.remove(r);
57498             }
57499         });
57500     },
57501
57502     // private
57503     onRemove : function(v, index, r){
57504         this.selections.remove(r);
57505     },
57506
57507     // private
57508     onRowUpdated : function(v, index, r){
57509         if(this.isSelected(r)){
57510             v.onRowSelect(index);
57511         }
57512     },
57513
57514     /**
57515      * Select records.
57516      * @param {Array} records The records to select
57517      * @param {Boolean} keepExisting (optional) True to keep existing selections
57518      */
57519     selectRecords : function(records, keepExisting){
57520         if(!keepExisting){
57521             this.clearSelections();
57522         }
57523         var ds = this.grid.dataSource;
57524         for(var i = 0, len = records.length; i < len; i++){
57525             this.selectRow(ds.indexOf(records[i]), true);
57526         }
57527     },
57528
57529     /**
57530      * Gets the number of selected rows.
57531      * @return {Number}
57532      */
57533     getCount : function(){
57534         return this.selections.length;
57535     },
57536
57537     /**
57538      * Selects the first row in the grid.
57539      */
57540     selectFirstRow : function(){
57541         this.selectRow(0);
57542     },
57543
57544     /**
57545      * Select the last row.
57546      * @param {Boolean} keepExisting (optional) True to keep existing selections
57547      */
57548     selectLastRow : function(keepExisting){
57549         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57550     },
57551
57552     /**
57553      * Selects the row immediately following the last selected row.
57554      * @param {Boolean} keepExisting (optional) True to keep existing selections
57555      */
57556     selectNext : function(keepExisting){
57557         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57558             this.selectRow(this.last+1, keepExisting);
57559             this.grid.getView().focusRow(this.last);
57560         }
57561     },
57562
57563     /**
57564      * Selects the row that precedes the last selected row.
57565      * @param {Boolean} keepExisting (optional) True to keep existing selections
57566      */
57567     selectPrevious : function(keepExisting){
57568         if(this.last){
57569             this.selectRow(this.last-1, keepExisting);
57570             this.grid.getView().focusRow(this.last);
57571         }
57572     },
57573
57574     /**
57575      * Returns the selected records
57576      * @return {Array} Array of selected records
57577      */
57578     getSelections : function(){
57579         return [].concat(this.selections.items);
57580     },
57581
57582     /**
57583      * Returns the first selected record.
57584      * @return {Record}
57585      */
57586     getSelected : function(){
57587         return this.selections.itemAt(0);
57588     },
57589
57590
57591     /**
57592      * Clears all selections.
57593      */
57594     clearSelections : function(fast){
57595         if(this.locked) {
57596             return;
57597         }
57598         if(fast !== true){
57599             var ds = this.grid.dataSource;
57600             var s = this.selections;
57601             s.each(function(r){
57602                 this.deselectRow(ds.indexOfId(r.id));
57603             }, this);
57604             s.clear();
57605         }else{
57606             this.selections.clear();
57607         }
57608         this.last = false;
57609     },
57610
57611
57612     /**
57613      * Selects all rows.
57614      */
57615     selectAll : function(){
57616         if(this.locked) {
57617             return;
57618         }
57619         this.selections.clear();
57620         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57621             this.selectRow(i, true);
57622         }
57623     },
57624
57625     /**
57626      * Returns True if there is a selection.
57627      * @return {Boolean}
57628      */
57629     hasSelection : function(){
57630         return this.selections.length > 0;
57631     },
57632
57633     /**
57634      * Returns True if the specified row is selected.
57635      * @param {Number/Record} record The record or index of the record to check
57636      * @return {Boolean}
57637      */
57638     isSelected : function(index){
57639         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57640         return (r && this.selections.key(r.id) ? true : false);
57641     },
57642
57643     /**
57644      * Returns True if the specified record id is selected.
57645      * @param {String} id The id of record to check
57646      * @return {Boolean}
57647      */
57648     isIdSelected : function(id){
57649         return (this.selections.key(id) ? true : false);
57650     },
57651
57652     // private
57653     handleMouseDown : function(e, t){
57654         var view = this.grid.getView(), rowIndex;
57655         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57656             return;
57657         };
57658         if(e.shiftKey && this.last !== false){
57659             var last = this.last;
57660             this.selectRange(last, rowIndex, e.ctrlKey);
57661             this.last = last; // reset the last
57662             view.focusRow(rowIndex);
57663         }else{
57664             var isSelected = this.isSelected(rowIndex);
57665             if(e.button !== 0 && isSelected){
57666                 view.focusRow(rowIndex);
57667             }else if(e.ctrlKey && isSelected){
57668                 this.deselectRow(rowIndex);
57669             }else if(!isSelected){
57670                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57671                 view.focusRow(rowIndex);
57672             }
57673         }
57674         this.fireEvent("afterselectionchange", this);
57675     },
57676     // private
57677     handleDragableRowClick :  function(grid, rowIndex, e) 
57678     {
57679         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57680             this.selectRow(rowIndex, false);
57681             grid.view.focusRow(rowIndex);
57682              this.fireEvent("afterselectionchange", this);
57683         }
57684     },
57685     
57686     /**
57687      * Selects multiple rows.
57688      * @param {Array} rows Array of the indexes of the row to select
57689      * @param {Boolean} keepExisting (optional) True to keep existing selections
57690      */
57691     selectRows : function(rows, keepExisting){
57692         if(!keepExisting){
57693             this.clearSelections();
57694         }
57695         for(var i = 0, len = rows.length; i < len; i++){
57696             this.selectRow(rows[i], true);
57697         }
57698     },
57699
57700     /**
57701      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57702      * @param {Number} startRow The index of the first row in the range
57703      * @param {Number} endRow The index of the last row in the range
57704      * @param {Boolean} keepExisting (optional) True to retain existing selections
57705      */
57706     selectRange : function(startRow, endRow, keepExisting){
57707         if(this.locked) {
57708             return;
57709         }
57710         if(!keepExisting){
57711             this.clearSelections();
57712         }
57713         if(startRow <= endRow){
57714             for(var i = startRow; i <= endRow; i++){
57715                 this.selectRow(i, true);
57716             }
57717         }else{
57718             for(var i = startRow; i >= endRow; i--){
57719                 this.selectRow(i, true);
57720             }
57721         }
57722     },
57723
57724     /**
57725      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57726      * @param {Number} startRow The index of the first row in the range
57727      * @param {Number} endRow The index of the last row in the range
57728      */
57729     deselectRange : function(startRow, endRow, preventViewNotify){
57730         if(this.locked) {
57731             return;
57732         }
57733         for(var i = startRow; i <= endRow; i++){
57734             this.deselectRow(i, preventViewNotify);
57735         }
57736     },
57737
57738     /**
57739      * Selects a row.
57740      * @param {Number} row The index of the row to select
57741      * @param {Boolean} keepExisting (optional) True to keep existing selections
57742      */
57743     selectRow : function(index, keepExisting, preventViewNotify){
57744         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57745             return;
57746         }
57747         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57748             if(!keepExisting || this.singleSelect){
57749                 this.clearSelections();
57750             }
57751             var r = this.grid.dataSource.getAt(index);
57752             this.selections.add(r);
57753             this.last = this.lastActive = index;
57754             if(!preventViewNotify){
57755                 this.grid.getView().onRowSelect(index);
57756             }
57757             this.fireEvent("rowselect", this, index, r);
57758             this.fireEvent("selectionchange", this);
57759         }
57760     },
57761
57762     /**
57763      * Deselects a row.
57764      * @param {Number} row The index of the row to deselect
57765      */
57766     deselectRow : function(index, preventViewNotify){
57767         if(this.locked) {
57768             return;
57769         }
57770         if(this.last == index){
57771             this.last = false;
57772         }
57773         if(this.lastActive == index){
57774             this.lastActive = false;
57775         }
57776         var r = this.grid.dataSource.getAt(index);
57777         this.selections.remove(r);
57778         if(!preventViewNotify){
57779             this.grid.getView().onRowDeselect(index);
57780         }
57781         this.fireEvent("rowdeselect", this, index);
57782         this.fireEvent("selectionchange", this);
57783     },
57784
57785     // private
57786     restoreLast : function(){
57787         if(this._last){
57788             this.last = this._last;
57789         }
57790     },
57791
57792     // private
57793     acceptsNav : function(row, col, cm){
57794         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57795     },
57796
57797     // private
57798     onEditorKey : function(field, e){
57799         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57800         if(k == e.TAB){
57801             e.stopEvent();
57802             ed.completeEdit();
57803             if(e.shiftKey){
57804                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57805             }else{
57806                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57807             }
57808         }else if(k == e.ENTER && !e.ctrlKey){
57809             e.stopEvent();
57810             ed.completeEdit();
57811             if(e.shiftKey){
57812                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57813             }else{
57814                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57815             }
57816         }else if(k == e.ESC){
57817             ed.cancelEdit();
57818         }
57819         if(newCell){
57820             g.startEditing(newCell[0], newCell[1]);
57821         }
57822     }
57823 });/*
57824  * Based on:
57825  * Ext JS Library 1.1.1
57826  * Copyright(c) 2006-2007, Ext JS, LLC.
57827  *
57828  * Originally Released Under LGPL - original licence link has changed is not relivant.
57829  *
57830  * Fork - LGPL
57831  * <script type="text/javascript">
57832  */
57833 /**
57834  * @class Roo.grid.CellSelectionModel
57835  * @extends Roo.grid.AbstractSelectionModel
57836  * This class provides the basic implementation for cell selection in a grid.
57837  * @constructor
57838  * @param {Object} config The object containing the configuration of this model.
57839  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57840  */
57841 Roo.grid.CellSelectionModel = function(config){
57842     Roo.apply(this, config);
57843
57844     this.selection = null;
57845
57846     this.addEvents({
57847         /**
57848              * @event beforerowselect
57849              * Fires before a cell is selected.
57850              * @param {SelectionModel} this
57851              * @param {Number} rowIndex The selected row index
57852              * @param {Number} colIndex The selected cell index
57853              */
57854             "beforecellselect" : true,
57855         /**
57856              * @event cellselect
57857              * Fires when a cell is selected.
57858              * @param {SelectionModel} this
57859              * @param {Number} rowIndex The selected row index
57860              * @param {Number} colIndex The selected cell index
57861              */
57862             "cellselect" : true,
57863         /**
57864              * @event selectionchange
57865              * Fires when the active selection changes.
57866              * @param {SelectionModel} this
57867              * @param {Object} selection null for no selection or an object (o) with two properties
57868                 <ul>
57869                 <li>o.record: the record object for the row the selection is in</li>
57870                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
57871                 </ul>
57872              */
57873             "selectionchange" : true,
57874         /**
57875              * @event tabend
57876              * Fires when the tab (or enter) was pressed on the last editable cell
57877              * You can use this to trigger add new row.
57878              * @param {SelectionModel} this
57879              */
57880             "tabend" : true,
57881          /**
57882              * @event beforeeditnext
57883              * Fires before the next editable sell is made active
57884              * You can use this to skip to another cell or fire the tabend
57885              *    if you set cell to false
57886              * @param {Object} eventdata object : { cell : [ row, col ] } 
57887              */
57888             "beforeeditnext" : true
57889     });
57890     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
57891 };
57892
57893 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
57894     
57895     enter_is_tab: false,
57896
57897     /** @ignore */
57898     initEvents : function(){
57899         this.grid.on("mousedown", this.handleMouseDown, this);
57900         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
57901         var view = this.grid.view;
57902         view.on("refresh", this.onViewChange, this);
57903         view.on("rowupdated", this.onRowUpdated, this);
57904         view.on("beforerowremoved", this.clearSelections, this);
57905         view.on("beforerowsinserted", this.clearSelections, this);
57906         if(this.grid.isEditor){
57907             this.grid.on("beforeedit", this.beforeEdit,  this);
57908         }
57909     },
57910
57911         //private
57912     beforeEdit : function(e){
57913         this.select(e.row, e.column, false, true, e.record);
57914     },
57915
57916         //private
57917     onRowUpdated : function(v, index, r){
57918         if(this.selection && this.selection.record == r){
57919             v.onCellSelect(index, this.selection.cell[1]);
57920         }
57921     },
57922
57923         //private
57924     onViewChange : function(){
57925         this.clearSelections(true);
57926     },
57927
57928         /**
57929          * Returns the currently selected cell,.
57930          * @return {Array} The selected cell (row, column) or null if none selected.
57931          */
57932     getSelectedCell : function(){
57933         return this.selection ? this.selection.cell : null;
57934     },
57935
57936     /**
57937      * Clears all selections.
57938      * @param {Boolean} true to prevent the gridview from being notified about the change.
57939      */
57940     clearSelections : function(preventNotify){
57941         var s = this.selection;
57942         if(s){
57943             if(preventNotify !== true){
57944                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
57945             }
57946             this.selection = null;
57947             this.fireEvent("selectionchange", this, null);
57948         }
57949     },
57950
57951     /**
57952      * Returns true if there is a selection.
57953      * @return {Boolean}
57954      */
57955     hasSelection : function(){
57956         return this.selection ? true : false;
57957     },
57958
57959     /** @ignore */
57960     handleMouseDown : function(e, t){
57961         var v = this.grid.getView();
57962         if(this.isLocked()){
57963             return;
57964         };
57965         var row = v.findRowIndex(t);
57966         var cell = v.findCellIndex(t);
57967         if(row !== false && cell !== false){
57968             this.select(row, cell);
57969         }
57970     },
57971
57972     /**
57973      * Selects a cell.
57974      * @param {Number} rowIndex
57975      * @param {Number} collIndex
57976      */
57977     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
57978         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
57979             this.clearSelections();
57980             r = r || this.grid.dataSource.getAt(rowIndex);
57981             this.selection = {
57982                 record : r,
57983                 cell : [rowIndex, colIndex]
57984             };
57985             if(!preventViewNotify){
57986                 var v = this.grid.getView();
57987                 v.onCellSelect(rowIndex, colIndex);
57988                 if(preventFocus !== true){
57989                     v.focusCell(rowIndex, colIndex);
57990                 }
57991             }
57992             this.fireEvent("cellselect", this, rowIndex, colIndex);
57993             this.fireEvent("selectionchange", this, this.selection);
57994         }
57995     },
57996
57997         //private
57998     isSelectable : function(rowIndex, colIndex, cm){
57999         return !cm.isHidden(colIndex);
58000     },
58001
58002     /** @ignore */
58003     handleKeyDown : function(e){
58004         //Roo.log('Cell Sel Model handleKeyDown');
58005         if(!e.isNavKeyPress()){
58006             return;
58007         }
58008         var g = this.grid, s = this.selection;
58009         if(!s){
58010             e.stopEvent();
58011             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58012             if(cell){
58013                 this.select(cell[0], cell[1]);
58014             }
58015             return;
58016         }
58017         var sm = this;
58018         var walk = function(row, col, step){
58019             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58020         };
58021         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58022         var newCell;
58023
58024       
58025
58026         switch(k){
58027             case e.TAB:
58028                 // handled by onEditorKey
58029                 if (g.isEditor && g.editing) {
58030                     return;
58031                 }
58032                 if(e.shiftKey) {
58033                     newCell = walk(r, c-1, -1);
58034                 } else {
58035                     newCell = walk(r, c+1, 1);
58036                 }
58037                 break;
58038             
58039             case e.DOWN:
58040                newCell = walk(r+1, c, 1);
58041                 break;
58042             
58043             case e.UP:
58044                 newCell = walk(r-1, c, -1);
58045                 break;
58046             
58047             case e.RIGHT:
58048                 newCell = walk(r, c+1, 1);
58049                 break;
58050             
58051             case e.LEFT:
58052                 newCell = walk(r, c-1, -1);
58053                 break;
58054             
58055             case e.ENTER:
58056                 
58057                 if(g.isEditor && !g.editing){
58058                    g.startEditing(r, c);
58059                    e.stopEvent();
58060                    return;
58061                 }
58062                 
58063                 
58064              break;
58065         };
58066         if(newCell){
58067             this.select(newCell[0], newCell[1]);
58068             e.stopEvent();
58069             
58070         }
58071     },
58072
58073     acceptsNav : function(row, col, cm){
58074         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58075     },
58076     /**
58077      * Selects a cell.
58078      * @param {Number} field (not used) - as it's normally used as a listener
58079      * @param {Number} e - event - fake it by using
58080      *
58081      * var e = Roo.EventObjectImpl.prototype;
58082      * e.keyCode = e.TAB
58083      *
58084      * 
58085      */
58086     onEditorKey : function(field, e){
58087         
58088         var k = e.getKey(),
58089             newCell,
58090             g = this.grid,
58091             ed = g.activeEditor,
58092             forward = false;
58093         ///Roo.log('onEditorKey' + k);
58094         
58095         
58096         if (this.enter_is_tab && k == e.ENTER) {
58097             k = e.TAB;
58098         }
58099         
58100         if(k == e.TAB){
58101             if(e.shiftKey){
58102                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58103             }else{
58104                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58105                 forward = true;
58106             }
58107             
58108             e.stopEvent();
58109             
58110         } else if(k == e.ENTER &&  !e.ctrlKey){
58111             ed.completeEdit();
58112             e.stopEvent();
58113             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58114         
58115                 } else if(k == e.ESC){
58116             ed.cancelEdit();
58117         }
58118                 
58119         if (newCell) {
58120             var ecall = { cell : newCell, forward : forward };
58121             this.fireEvent('beforeeditnext', ecall );
58122             newCell = ecall.cell;
58123                         forward = ecall.forward;
58124         }
58125                 
58126         if(newCell){
58127             //Roo.log('next cell after edit');
58128             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58129         } else if (forward) {
58130             // tabbed past last
58131             this.fireEvent.defer(100, this, ['tabend',this]);
58132         }
58133     }
58134 });/*
58135  * Based on:
58136  * Ext JS Library 1.1.1
58137  * Copyright(c) 2006-2007, Ext JS, LLC.
58138  *
58139  * Originally Released Under LGPL - original licence link has changed is not relivant.
58140  *
58141  * Fork - LGPL
58142  * <script type="text/javascript">
58143  */
58144  
58145 /**
58146  * @class Roo.grid.EditorGrid
58147  * @extends Roo.grid.Grid
58148  * Class for creating and editable grid.
58149  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58150  * The container MUST have some type of size defined for the grid to fill. The container will be 
58151  * automatically set to position relative if it isn't already.
58152  * @param {Object} dataSource The data model to bind to
58153  * @param {Object} colModel The column model with info about this grid's columns
58154  */
58155 Roo.grid.EditorGrid = function(container, config){
58156     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58157     this.getGridEl().addClass("xedit-grid");
58158
58159     if(!this.selModel){
58160         this.selModel = new Roo.grid.CellSelectionModel();
58161     }
58162
58163     this.activeEditor = null;
58164
58165         this.addEvents({
58166             /**
58167              * @event beforeedit
58168              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58169              * <ul style="padding:5px;padding-left:16px;">
58170              * <li>grid - This grid</li>
58171              * <li>record - The record being edited</li>
58172              * <li>field - The field name being edited</li>
58173              * <li>value - The value for the field being edited.</li>
58174              * <li>row - The grid row index</li>
58175              * <li>column - The grid column index</li>
58176              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58177              * </ul>
58178              * @param {Object} e An edit event (see above for description)
58179              */
58180             "beforeedit" : true,
58181             /**
58182              * @event afteredit
58183              * Fires after a cell is edited. <br />
58184              * <ul style="padding:5px;padding-left:16px;">
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              * </ul>
58193              * @param {Object} e An edit event (see above for description)
58194              */
58195             "afteredit" : true,
58196             /**
58197              * @event validateedit
58198              * Fires after a cell is edited, but before the value is set in the record. 
58199          * You can use this to modify the value being set in the field, Return false
58200              * to cancel the change. The edit event object has the following properties <br />
58201              * <ul style="padding:5px;padding-left:16px;">
58202          * <li>editor - This editor</li>
58203              * <li>grid - This grid</li>
58204              * <li>record - The record being edited</li>
58205              * <li>field - The field name being edited</li>
58206              * <li>value - The value being set</li>
58207              * <li>originalValue - The original value for the field, before the edit.</li>
58208              * <li>row - The grid row index</li>
58209              * <li>column - The grid column index</li>
58210              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58211              * </ul>
58212              * @param {Object} e An edit event (see above for description)
58213              */
58214             "validateedit" : true
58215         });
58216     this.on("bodyscroll", this.stopEditing,  this);
58217     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58218 };
58219
58220 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58221     /**
58222      * @cfg {Number} clicksToEdit
58223      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58224      */
58225     clicksToEdit: 2,
58226
58227     // private
58228     isEditor : true,
58229     // private
58230     trackMouseOver: false, // causes very odd FF errors
58231
58232     onCellDblClick : function(g, row, col){
58233         this.startEditing(row, col);
58234     },
58235
58236     onEditComplete : function(ed, value, startValue){
58237         this.editing = false;
58238         this.activeEditor = null;
58239         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58240         var r = ed.record;
58241         var field = this.colModel.getDataIndex(ed.col);
58242         var e = {
58243             grid: this,
58244             record: r,
58245             field: field,
58246             originalValue: startValue,
58247             value: value,
58248             row: ed.row,
58249             column: ed.col,
58250             cancel:false,
58251             editor: ed
58252         };
58253         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58254         cell.show();
58255           
58256         if(String(value) !== String(startValue)){
58257             
58258             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58259                 r.set(field, e.value);
58260                 // if we are dealing with a combo box..
58261                 // then we also set the 'name' colum to be the displayField
58262                 if (ed.field.displayField && ed.field.name) {
58263                     r.set(ed.field.name, ed.field.el.dom.value);
58264                 }
58265                 
58266                 delete e.cancel; //?? why!!!
58267                 this.fireEvent("afteredit", e);
58268             }
58269         } else {
58270             this.fireEvent("afteredit", e); // always fire it!
58271         }
58272         this.view.focusCell(ed.row, ed.col);
58273     },
58274
58275     /**
58276      * Starts editing the specified for the specified row/column
58277      * @param {Number} rowIndex
58278      * @param {Number} colIndex
58279      */
58280     startEditing : function(row, col){
58281         this.stopEditing();
58282         if(this.colModel.isCellEditable(col, row)){
58283             this.view.ensureVisible(row, col, true);
58284           
58285             var r = this.dataSource.getAt(row);
58286             var field = this.colModel.getDataIndex(col);
58287             var cell = Roo.get(this.view.getCell(row,col));
58288             var e = {
58289                 grid: this,
58290                 record: r,
58291                 field: field,
58292                 value: r.data[field],
58293                 row: row,
58294                 column: col,
58295                 cancel:false 
58296             };
58297             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58298                 this.editing = true;
58299                 var ed = this.colModel.getCellEditor(col, row);
58300                 
58301                 if (!ed) {
58302                     return;
58303                 }
58304                 if(!ed.rendered){
58305                     ed.render(ed.parentEl || document.body);
58306                 }
58307                 ed.field.reset();
58308                
58309                 cell.hide();
58310                 
58311                 (function(){ // complex but required for focus issues in safari, ie and opera
58312                     ed.row = row;
58313                     ed.col = col;
58314                     ed.record = r;
58315                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58316                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58317                     this.activeEditor = ed;
58318                     var v = r.data[field];
58319                     ed.startEdit(this.view.getCell(row, col), v);
58320                     // combo's with 'displayField and name set
58321                     if (ed.field.displayField && ed.field.name) {
58322                         ed.field.el.dom.value = r.data[ed.field.name];
58323                     }
58324                     
58325                     
58326                 }).defer(50, this);
58327             }
58328         }
58329     },
58330         
58331     /**
58332      * Stops any active editing
58333      */
58334     stopEditing : function(){
58335         if(this.activeEditor){
58336             this.activeEditor.completeEdit();
58337         }
58338         this.activeEditor = null;
58339     },
58340         
58341          /**
58342      * Called to get grid's drag proxy text, by default returns this.ddText.
58343      * @return {String}
58344      */
58345     getDragDropText : function(){
58346         var count = this.selModel.getSelectedCell() ? 1 : 0;
58347         return String.format(this.ddText, count, count == 1 ? '' : 's');
58348     }
58349         
58350 });/*
58351  * Based on:
58352  * Ext JS Library 1.1.1
58353  * Copyright(c) 2006-2007, Ext JS, LLC.
58354  *
58355  * Originally Released Under LGPL - original licence link has changed is not relivant.
58356  *
58357  * Fork - LGPL
58358  * <script type="text/javascript">
58359  */
58360
58361 // private - not really -- you end up using it !
58362 // This is a support class used internally by the Grid components
58363
58364 /**
58365  * @class Roo.grid.GridEditor
58366  * @extends Roo.Editor
58367  * Class for creating and editable grid elements.
58368  * @param {Object} config any settings (must include field)
58369  */
58370 Roo.grid.GridEditor = function(field, config){
58371     if (!config && field.field) {
58372         config = field;
58373         field = Roo.factory(config.field, Roo.form);
58374     }
58375     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58376     field.monitorTab = false;
58377 };
58378
58379 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58380     
58381     /**
58382      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58383      */
58384     
58385     alignment: "tl-tl",
58386     autoSize: "width",
58387     hideEl : false,
58388     cls: "x-small-editor x-grid-editor",
58389     shim:false,
58390     shadow:"frame"
58391 });/*
58392  * Based on:
58393  * Ext JS Library 1.1.1
58394  * Copyright(c) 2006-2007, Ext JS, LLC.
58395  *
58396  * Originally Released Under LGPL - original licence link has changed is not relivant.
58397  *
58398  * Fork - LGPL
58399  * <script type="text/javascript">
58400  */
58401   
58402
58403   
58404 Roo.grid.PropertyRecord = Roo.data.Record.create([
58405     {name:'name',type:'string'},  'value'
58406 ]);
58407
58408
58409 Roo.grid.PropertyStore = function(grid, source){
58410     this.grid = grid;
58411     this.store = new Roo.data.Store({
58412         recordType : Roo.grid.PropertyRecord
58413     });
58414     this.store.on('update', this.onUpdate,  this);
58415     if(source){
58416         this.setSource(source);
58417     }
58418     Roo.grid.PropertyStore.superclass.constructor.call(this);
58419 };
58420
58421
58422
58423 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58424     setSource : function(o){
58425         this.source = o;
58426         this.store.removeAll();
58427         var data = [];
58428         for(var k in o){
58429             if(this.isEditableValue(o[k])){
58430                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58431             }
58432         }
58433         this.store.loadRecords({records: data}, {}, true);
58434     },
58435
58436     onUpdate : function(ds, record, type){
58437         if(type == Roo.data.Record.EDIT){
58438             var v = record.data['value'];
58439             var oldValue = record.modified['value'];
58440             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58441                 this.source[record.id] = v;
58442                 record.commit();
58443                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58444             }else{
58445                 record.reject();
58446             }
58447         }
58448     },
58449
58450     getProperty : function(row){
58451        return this.store.getAt(row);
58452     },
58453
58454     isEditableValue: function(val){
58455         if(val && val instanceof Date){
58456             return true;
58457         }else if(typeof val == 'object' || typeof val == 'function'){
58458             return false;
58459         }
58460         return true;
58461     },
58462
58463     setValue : function(prop, value){
58464         this.source[prop] = value;
58465         this.store.getById(prop).set('value', value);
58466     },
58467
58468     getSource : function(){
58469         return this.source;
58470     }
58471 });
58472
58473 Roo.grid.PropertyColumnModel = function(grid, store){
58474     this.grid = grid;
58475     var g = Roo.grid;
58476     g.PropertyColumnModel.superclass.constructor.call(this, [
58477         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58478         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58479     ]);
58480     this.store = store;
58481     this.bselect = Roo.DomHelper.append(document.body, {
58482         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58483             {tag: 'option', value: 'true', html: 'true'},
58484             {tag: 'option', value: 'false', html: 'false'}
58485         ]
58486     });
58487     Roo.id(this.bselect);
58488     var f = Roo.form;
58489     this.editors = {
58490         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58491         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58492         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58493         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58494         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58495     };
58496     this.renderCellDelegate = this.renderCell.createDelegate(this);
58497     this.renderPropDelegate = this.renderProp.createDelegate(this);
58498 };
58499
58500 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58501     
58502     
58503     nameText : 'Name',
58504     valueText : 'Value',
58505     
58506     dateFormat : 'm/j/Y',
58507     
58508     
58509     renderDate : function(dateVal){
58510         return dateVal.dateFormat(this.dateFormat);
58511     },
58512
58513     renderBool : function(bVal){
58514         return bVal ? 'true' : 'false';
58515     },
58516
58517     isCellEditable : function(colIndex, rowIndex){
58518         return colIndex == 1;
58519     },
58520
58521     getRenderer : function(col){
58522         return col == 1 ?
58523             this.renderCellDelegate : this.renderPropDelegate;
58524     },
58525
58526     renderProp : function(v){
58527         return this.getPropertyName(v);
58528     },
58529
58530     renderCell : function(val){
58531         var rv = val;
58532         if(val instanceof Date){
58533             rv = this.renderDate(val);
58534         }else if(typeof val == 'boolean'){
58535             rv = this.renderBool(val);
58536         }
58537         return Roo.util.Format.htmlEncode(rv);
58538     },
58539
58540     getPropertyName : function(name){
58541         var pn = this.grid.propertyNames;
58542         return pn && pn[name] ? pn[name] : name;
58543     },
58544
58545     getCellEditor : function(colIndex, rowIndex){
58546         var p = this.store.getProperty(rowIndex);
58547         var n = p.data['name'], val = p.data['value'];
58548         
58549         if(typeof(this.grid.customEditors[n]) == 'string'){
58550             return this.editors[this.grid.customEditors[n]];
58551         }
58552         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58553             return this.grid.customEditors[n];
58554         }
58555         if(val instanceof Date){
58556             return this.editors['date'];
58557         }else if(typeof val == 'number'){
58558             return this.editors['number'];
58559         }else if(typeof val == 'boolean'){
58560             return this.editors['boolean'];
58561         }else{
58562             return this.editors['string'];
58563         }
58564     }
58565 });
58566
58567 /**
58568  * @class Roo.grid.PropertyGrid
58569  * @extends Roo.grid.EditorGrid
58570  * This class represents the  interface of a component based property grid control.
58571  * <br><br>Usage:<pre><code>
58572  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58573       
58574  });
58575  // set any options
58576  grid.render();
58577  * </code></pre>
58578   
58579  * @constructor
58580  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58581  * The container MUST have some type of size defined for the grid to fill. The container will be
58582  * automatically set to position relative if it isn't already.
58583  * @param {Object} config A config object that sets properties on this grid.
58584  */
58585 Roo.grid.PropertyGrid = function(container, config){
58586     config = config || {};
58587     var store = new Roo.grid.PropertyStore(this);
58588     this.store = store;
58589     var cm = new Roo.grid.PropertyColumnModel(this, store);
58590     store.store.sort('name', 'ASC');
58591     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58592         ds: store.store,
58593         cm: cm,
58594         enableColLock:false,
58595         enableColumnMove:false,
58596         stripeRows:false,
58597         trackMouseOver: false,
58598         clicksToEdit:1
58599     }, config));
58600     this.getGridEl().addClass('x-props-grid');
58601     this.lastEditRow = null;
58602     this.on('columnresize', this.onColumnResize, this);
58603     this.addEvents({
58604          /**
58605              * @event beforepropertychange
58606              * Fires before a property changes (return false to stop?)
58607              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58608              * @param {String} id Record Id
58609              * @param {String} newval New Value
58610          * @param {String} oldval Old Value
58611              */
58612         "beforepropertychange": true,
58613         /**
58614              * @event propertychange
58615              * Fires after a property changes
58616              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58617              * @param {String} id Record Id
58618              * @param {String} newval New Value
58619          * @param {String} oldval Old Value
58620              */
58621         "propertychange": true
58622     });
58623     this.customEditors = this.customEditors || {};
58624 };
58625 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58626     
58627      /**
58628      * @cfg {Object} customEditors map of colnames=> custom editors.
58629      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58630      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58631      * false disables editing of the field.
58632          */
58633     
58634       /**
58635      * @cfg {Object} propertyNames map of property Names to their displayed value
58636          */
58637     
58638     render : function(){
58639         Roo.grid.PropertyGrid.superclass.render.call(this);
58640         this.autoSize.defer(100, this);
58641     },
58642
58643     autoSize : function(){
58644         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58645         if(this.view){
58646             this.view.fitColumns();
58647         }
58648     },
58649
58650     onColumnResize : function(){
58651         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58652         this.autoSize();
58653     },
58654     /**
58655      * Sets the data for the Grid
58656      * accepts a Key => Value object of all the elements avaiable.
58657      * @param {Object} data  to appear in grid.
58658      */
58659     setSource : function(source){
58660         this.store.setSource(source);
58661         //this.autoSize();
58662     },
58663     /**
58664      * Gets all the data from the grid.
58665      * @return {Object} data  data stored in grid
58666      */
58667     getSource : function(){
58668         return this.store.getSource();
58669     }
58670 });/*
58671   
58672  * Licence LGPL
58673  
58674  */
58675  
58676 /**
58677  * @class Roo.grid.Calendar
58678  * @extends Roo.util.Grid
58679  * This class extends the Grid to provide a calendar widget
58680  * <br><br>Usage:<pre><code>
58681  var grid = new Roo.grid.Calendar("my-container-id", {
58682      ds: myDataStore,
58683      cm: myColModel,
58684      selModel: mySelectionModel,
58685      autoSizeColumns: true,
58686      monitorWindowResize: false,
58687      trackMouseOver: true
58688      eventstore : real data store..
58689  });
58690  // set any options
58691  grid.render();
58692   
58693   * @constructor
58694  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58695  * The container MUST have some type of size defined for the grid to fill. The container will be
58696  * automatically set to position relative if it isn't already.
58697  * @param {Object} config A config object that sets properties on this grid.
58698  */
58699 Roo.grid.Calendar = function(container, config){
58700         // initialize the container
58701         this.container = Roo.get(container);
58702         this.container.update("");
58703         this.container.setStyle("overflow", "hidden");
58704     this.container.addClass('x-grid-container');
58705
58706     this.id = this.container.id;
58707
58708     Roo.apply(this, config);
58709     // check and correct shorthanded configs
58710     
58711     var rows = [];
58712     var d =1;
58713     for (var r = 0;r < 6;r++) {
58714         
58715         rows[r]=[];
58716         for (var c =0;c < 7;c++) {
58717             rows[r][c]= '';
58718         }
58719     }
58720     if (this.eventStore) {
58721         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58722         this.eventStore.on('load',this.onLoad, this);
58723         this.eventStore.on('beforeload',this.clearEvents, this);
58724          
58725     }
58726     
58727     this.dataSource = new Roo.data.Store({
58728             proxy: new Roo.data.MemoryProxy(rows),
58729             reader: new Roo.data.ArrayReader({}, [
58730                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58731     });
58732
58733     this.dataSource.load();
58734     this.ds = this.dataSource;
58735     this.ds.xmodule = this.xmodule || false;
58736     
58737     
58738     var cellRender = function(v,x,r)
58739     {
58740         return String.format(
58741             '<div class="fc-day  fc-widget-content"><div>' +
58742                 '<div class="fc-event-container"></div>' +
58743                 '<div class="fc-day-number">{0}</div>'+
58744                 
58745                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58746             '</div></div>', v);
58747     
58748     }
58749     
58750     
58751     this.colModel = new Roo.grid.ColumnModel( [
58752         {
58753             xtype: 'ColumnModel',
58754             xns: Roo.grid,
58755             dataIndex : 'weekday0',
58756             header : 'Sunday',
58757             renderer : cellRender
58758         },
58759         {
58760             xtype: 'ColumnModel',
58761             xns: Roo.grid,
58762             dataIndex : 'weekday1',
58763             header : 'Monday',
58764             renderer : cellRender
58765         },
58766         {
58767             xtype: 'ColumnModel',
58768             xns: Roo.grid,
58769             dataIndex : 'weekday2',
58770             header : 'Tuesday',
58771             renderer : cellRender
58772         },
58773         {
58774             xtype: 'ColumnModel',
58775             xns: Roo.grid,
58776             dataIndex : 'weekday3',
58777             header : 'Wednesday',
58778             renderer : cellRender
58779         },
58780         {
58781             xtype: 'ColumnModel',
58782             xns: Roo.grid,
58783             dataIndex : 'weekday4',
58784             header : 'Thursday',
58785             renderer : cellRender
58786         },
58787         {
58788             xtype: 'ColumnModel',
58789             xns: Roo.grid,
58790             dataIndex : 'weekday5',
58791             header : 'Friday',
58792             renderer : cellRender
58793         },
58794         {
58795             xtype: 'ColumnModel',
58796             xns: Roo.grid,
58797             dataIndex : 'weekday6',
58798             header : 'Saturday',
58799             renderer : cellRender
58800         }
58801     ]);
58802     this.cm = this.colModel;
58803     this.cm.xmodule = this.xmodule || false;
58804  
58805         
58806           
58807     //this.selModel = new Roo.grid.CellSelectionModel();
58808     //this.sm = this.selModel;
58809     //this.selModel.init(this);
58810     
58811     
58812     if(this.width){
58813         this.container.setWidth(this.width);
58814     }
58815
58816     if(this.height){
58817         this.container.setHeight(this.height);
58818     }
58819     /** @private */
58820         this.addEvents({
58821         // raw events
58822         /**
58823          * @event click
58824          * The raw click event for the entire grid.
58825          * @param {Roo.EventObject} e
58826          */
58827         "click" : true,
58828         /**
58829          * @event dblclick
58830          * The raw dblclick event for the entire grid.
58831          * @param {Roo.EventObject} e
58832          */
58833         "dblclick" : true,
58834         /**
58835          * @event contextmenu
58836          * The raw contextmenu event for the entire grid.
58837          * @param {Roo.EventObject} e
58838          */
58839         "contextmenu" : true,
58840         /**
58841          * @event mousedown
58842          * The raw mousedown event for the entire grid.
58843          * @param {Roo.EventObject} e
58844          */
58845         "mousedown" : true,
58846         /**
58847          * @event mouseup
58848          * The raw mouseup event for the entire grid.
58849          * @param {Roo.EventObject} e
58850          */
58851         "mouseup" : true,
58852         /**
58853          * @event mouseover
58854          * The raw mouseover event for the entire grid.
58855          * @param {Roo.EventObject} e
58856          */
58857         "mouseover" : true,
58858         /**
58859          * @event mouseout
58860          * The raw mouseout event for the entire grid.
58861          * @param {Roo.EventObject} e
58862          */
58863         "mouseout" : true,
58864         /**
58865          * @event keypress
58866          * The raw keypress event for the entire grid.
58867          * @param {Roo.EventObject} e
58868          */
58869         "keypress" : true,
58870         /**
58871          * @event keydown
58872          * The raw keydown event for the entire grid.
58873          * @param {Roo.EventObject} e
58874          */
58875         "keydown" : true,
58876
58877         // custom events
58878
58879         /**
58880          * @event cellclick
58881          * Fires when a cell is clicked
58882          * @param {Grid} this
58883          * @param {Number} rowIndex
58884          * @param {Number} columnIndex
58885          * @param {Roo.EventObject} e
58886          */
58887         "cellclick" : true,
58888         /**
58889          * @event celldblclick
58890          * Fires when a cell is double clicked
58891          * @param {Grid} this
58892          * @param {Number} rowIndex
58893          * @param {Number} columnIndex
58894          * @param {Roo.EventObject} e
58895          */
58896         "celldblclick" : true,
58897         /**
58898          * @event rowclick
58899          * Fires when a row is clicked
58900          * @param {Grid} this
58901          * @param {Number} rowIndex
58902          * @param {Roo.EventObject} e
58903          */
58904         "rowclick" : true,
58905         /**
58906          * @event rowdblclick
58907          * Fires when a row is double clicked
58908          * @param {Grid} this
58909          * @param {Number} rowIndex
58910          * @param {Roo.EventObject} e
58911          */
58912         "rowdblclick" : true,
58913         /**
58914          * @event headerclick
58915          * Fires when a header is clicked
58916          * @param {Grid} this
58917          * @param {Number} columnIndex
58918          * @param {Roo.EventObject} e
58919          */
58920         "headerclick" : true,
58921         /**
58922          * @event headerdblclick
58923          * Fires when a header cell is double clicked
58924          * @param {Grid} this
58925          * @param {Number} columnIndex
58926          * @param {Roo.EventObject} e
58927          */
58928         "headerdblclick" : true,
58929         /**
58930          * @event rowcontextmenu
58931          * Fires when a row is right clicked
58932          * @param {Grid} this
58933          * @param {Number} rowIndex
58934          * @param {Roo.EventObject} e
58935          */
58936         "rowcontextmenu" : true,
58937         /**
58938          * @event cellcontextmenu
58939          * Fires when a cell is right clicked
58940          * @param {Grid} this
58941          * @param {Number} rowIndex
58942          * @param {Number} cellIndex
58943          * @param {Roo.EventObject} e
58944          */
58945          "cellcontextmenu" : true,
58946         /**
58947          * @event headercontextmenu
58948          * Fires when a header is right clicked
58949          * @param {Grid} this
58950          * @param {Number} columnIndex
58951          * @param {Roo.EventObject} e
58952          */
58953         "headercontextmenu" : true,
58954         /**
58955          * @event bodyscroll
58956          * Fires when the body element is scrolled
58957          * @param {Number} scrollLeft
58958          * @param {Number} scrollTop
58959          */
58960         "bodyscroll" : true,
58961         /**
58962          * @event columnresize
58963          * Fires when the user resizes a column
58964          * @param {Number} columnIndex
58965          * @param {Number} newSize
58966          */
58967         "columnresize" : true,
58968         /**
58969          * @event columnmove
58970          * Fires when the user moves a column
58971          * @param {Number} oldIndex
58972          * @param {Number} newIndex
58973          */
58974         "columnmove" : true,
58975         /**
58976          * @event startdrag
58977          * Fires when row(s) start being dragged
58978          * @param {Grid} this
58979          * @param {Roo.GridDD} dd The drag drop object
58980          * @param {event} e The raw browser event
58981          */
58982         "startdrag" : true,
58983         /**
58984          * @event enddrag
58985          * Fires when a drag operation is complete
58986          * @param {Grid} this
58987          * @param {Roo.GridDD} dd The drag drop object
58988          * @param {event} e The raw browser event
58989          */
58990         "enddrag" : true,
58991         /**
58992          * @event dragdrop
58993          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
59000         /**
59001          * @event dragover
59002          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
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         "dragover" : true,
59009         /**
59010          * @event dragenter
59011          *  Fires when the dragged row(s) first cross another DD target while being dragged
59012          * @param {Grid} this
59013          * @param {Roo.GridDD} dd The drag drop object
59014          * @param {String} targetId The target drag drop object
59015          * @param {event} e The raw browser event
59016          */
59017         "dragenter" : true,
59018         /**
59019          * @event dragout
59020          * Fires when the dragged row(s) leave another DD target while being dragged
59021          * @param {Grid} this
59022          * @param {Roo.GridDD} dd The drag drop object
59023          * @param {String} targetId The target drag drop object
59024          * @param {event} e The raw browser event
59025          */
59026         "dragout" : true,
59027         /**
59028          * @event rowclass
59029          * Fires when a row is rendered, so you can change add a style to it.
59030          * @param {GridView} gridview   The grid view
59031          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59032          */
59033         'rowclass' : true,
59034
59035         /**
59036          * @event render
59037          * Fires when the grid is rendered
59038          * @param {Grid} grid
59039          */
59040         'render' : true,
59041             /**
59042              * @event select
59043              * Fires when a date is selected
59044              * @param {DatePicker} this
59045              * @param {Date} date The selected date
59046              */
59047         'select': true,
59048         /**
59049              * @event monthchange
59050              * Fires when the displayed month changes 
59051              * @param {DatePicker} this
59052              * @param {Date} date The selected month
59053              */
59054         'monthchange': true,
59055         /**
59056              * @event evententer
59057              * Fires when mouse over an event
59058              * @param {Calendar} this
59059              * @param {event} Event
59060              */
59061         'evententer': true,
59062         /**
59063              * @event eventleave
59064              * Fires when the mouse leaves an
59065              * @param {Calendar} this
59066              * @param {event}
59067              */
59068         'eventleave': true,
59069         /**
59070              * @event eventclick
59071              * Fires when the mouse click an
59072              * @param {Calendar} this
59073              * @param {event}
59074              */
59075         'eventclick': true,
59076         /**
59077              * @event eventrender
59078              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59079              * @param {Calendar} this
59080              * @param {data} data to be modified
59081              */
59082         'eventrender': true
59083         
59084     });
59085
59086     Roo.grid.Grid.superclass.constructor.call(this);
59087     this.on('render', function() {
59088         this.view.el.addClass('x-grid-cal'); 
59089         
59090         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59091
59092     },this);
59093     
59094     if (!Roo.grid.Calendar.style) {
59095         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59096             
59097             
59098             '.x-grid-cal .x-grid-col' :  {
59099                 height: 'auto !important',
59100                 'vertical-align': 'top'
59101             },
59102             '.x-grid-cal  .fc-event-hori' : {
59103                 height: '14px'
59104             }
59105              
59106             
59107         }, Roo.id());
59108     }
59109
59110     
59111     
59112 };
59113 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59114     /**
59115      * @cfg {Store} eventStore The store that loads events.
59116      */
59117     eventStore : 25,
59118
59119      
59120     activeDate : false,
59121     startDay : 0,
59122     autoWidth : true,
59123     monitorWindowResize : false,
59124
59125     
59126     resizeColumns : function() {
59127         var col = (this.view.el.getWidth() / 7) - 3;
59128         // loop through cols, and setWidth
59129         for(var i =0 ; i < 7 ; i++){
59130             this.cm.setColumnWidth(i, col);
59131         }
59132     },
59133      setDate :function(date) {
59134         
59135         Roo.log('setDate?');
59136         
59137         this.resizeColumns();
59138         var vd = this.activeDate;
59139         this.activeDate = date;
59140 //        if(vd && this.el){
59141 //            var t = date.getTime();
59142 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59143 //                Roo.log('using add remove');
59144 //                
59145 //                this.fireEvent('monthchange', this, date);
59146 //                
59147 //                this.cells.removeClass("fc-state-highlight");
59148 //                this.cells.each(function(c){
59149 //                   if(c.dateValue == t){
59150 //                       c.addClass("fc-state-highlight");
59151 //                       setTimeout(function(){
59152 //                            try{c.dom.firstChild.focus();}catch(e){}
59153 //                       }, 50);
59154 //                       return false;
59155 //                   }
59156 //                   return true;
59157 //                });
59158 //                return;
59159 //            }
59160 //        }
59161         
59162         var days = date.getDaysInMonth();
59163         
59164         var firstOfMonth = date.getFirstDateOfMonth();
59165         var startingPos = firstOfMonth.getDay()-this.startDay;
59166         
59167         if(startingPos < this.startDay){
59168             startingPos += 7;
59169         }
59170         
59171         var pm = date.add(Date.MONTH, -1);
59172         var prevStart = pm.getDaysInMonth()-startingPos;
59173 //        
59174         
59175         
59176         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59177         
59178         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59179         //this.cells.addClassOnOver('fc-state-hover');
59180         
59181         var cells = this.cells.elements;
59182         var textEls = this.textNodes;
59183         
59184         //Roo.each(cells, function(cell){
59185         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59186         //});
59187         
59188         days += startingPos;
59189
59190         // convert everything to numbers so it's fast
59191         var day = 86400000;
59192         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59193         //Roo.log(d);
59194         //Roo.log(pm);
59195         //Roo.log(prevStart);
59196         
59197         var today = new Date().clearTime().getTime();
59198         var sel = date.clearTime().getTime();
59199         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59200         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59201         var ddMatch = this.disabledDatesRE;
59202         var ddText = this.disabledDatesText;
59203         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59204         var ddaysText = this.disabledDaysText;
59205         var format = this.format;
59206         
59207         var setCellClass = function(cal, cell){
59208             
59209             //Roo.log('set Cell Class');
59210             cell.title = "";
59211             var t = d.getTime();
59212             
59213             //Roo.log(d);
59214             
59215             
59216             cell.dateValue = t;
59217             if(t == today){
59218                 cell.className += " fc-today";
59219                 cell.className += " fc-state-highlight";
59220                 cell.title = cal.todayText;
59221             }
59222             if(t == sel){
59223                 // disable highlight in other month..
59224                 cell.className += " fc-state-highlight";
59225                 
59226             }
59227             // disabling
59228             if(t < min) {
59229                 //cell.className = " fc-state-disabled";
59230                 cell.title = cal.minText;
59231                 return;
59232             }
59233             if(t > max) {
59234                 //cell.className = " fc-state-disabled";
59235                 cell.title = cal.maxText;
59236                 return;
59237             }
59238             if(ddays){
59239                 if(ddays.indexOf(d.getDay()) != -1){
59240                     // cell.title = ddaysText;
59241                    // cell.className = " fc-state-disabled";
59242                 }
59243             }
59244             if(ddMatch && format){
59245                 var fvalue = d.dateFormat(format);
59246                 if(ddMatch.test(fvalue)){
59247                     cell.title = ddText.replace("%0", fvalue);
59248                    cell.className = " fc-state-disabled";
59249                 }
59250             }
59251             
59252             if (!cell.initialClassName) {
59253                 cell.initialClassName = cell.dom.className;
59254             }
59255             
59256             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59257         };
59258
59259         var i = 0;
59260         
59261         for(; i < startingPos; i++) {
59262             cells[i].dayName =  (++prevStart);
59263             Roo.log(textEls[i]);
59264             d.setDate(d.getDate()+1);
59265             
59266             //cells[i].className = "fc-past fc-other-month";
59267             setCellClass(this, cells[i]);
59268         }
59269         
59270         var intDay = 0;
59271         
59272         for(; i < days; i++){
59273             intDay = i - startingPos + 1;
59274             cells[i].dayName =  (intDay);
59275             d.setDate(d.getDate()+1);
59276             
59277             cells[i].className = ''; // "x-date-active";
59278             setCellClass(this, cells[i]);
59279         }
59280         var extraDays = 0;
59281         
59282         for(; i < 42; i++) {
59283             //textEls[i].innerHTML = (++extraDays);
59284             
59285             d.setDate(d.getDate()+1);
59286             cells[i].dayName = (++extraDays);
59287             cells[i].className = "fc-future fc-other-month";
59288             setCellClass(this, cells[i]);
59289         }
59290         
59291         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59292         
59293         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59294         
59295         // this will cause all the cells to mis
59296         var rows= [];
59297         var i =0;
59298         for (var r = 0;r < 6;r++) {
59299             for (var c =0;c < 7;c++) {
59300                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59301             }    
59302         }
59303         
59304         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59305         for(i=0;i<cells.length;i++) {
59306             
59307             this.cells.elements[i].dayName = cells[i].dayName ;
59308             this.cells.elements[i].className = cells[i].className;
59309             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59310             this.cells.elements[i].title = cells[i].title ;
59311             this.cells.elements[i].dateValue = cells[i].dateValue ;
59312         }
59313         
59314         
59315         
59316         
59317         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59318         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59319         
59320         ////if(totalRows != 6){
59321             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59322            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59323        // }
59324         
59325         this.fireEvent('monthchange', this, date);
59326         
59327         
59328     },
59329  /**
59330      * Returns the grid's SelectionModel.
59331      * @return {SelectionModel}
59332      */
59333     getSelectionModel : function(){
59334         if(!this.selModel){
59335             this.selModel = new Roo.grid.CellSelectionModel();
59336         }
59337         return this.selModel;
59338     },
59339
59340     load: function() {
59341         this.eventStore.load()
59342         
59343         
59344         
59345     },
59346     
59347     findCell : function(dt) {
59348         dt = dt.clearTime().getTime();
59349         var ret = false;
59350         this.cells.each(function(c){
59351             //Roo.log("check " +c.dateValue + '?=' + dt);
59352             if(c.dateValue == dt){
59353                 ret = c;
59354                 return false;
59355             }
59356             return true;
59357         });
59358         
59359         return ret;
59360     },
59361     
59362     findCells : function(rec) {
59363         var s = rec.data.start_dt.clone().clearTime().getTime();
59364        // Roo.log(s);
59365         var e= rec.data.end_dt.clone().clearTime().getTime();
59366        // Roo.log(e);
59367         var ret = [];
59368         this.cells.each(function(c){
59369              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59370             
59371             if(c.dateValue > e){
59372                 return ;
59373             }
59374             if(c.dateValue < s){
59375                 return ;
59376             }
59377             ret.push(c);
59378         });
59379         
59380         return ret;    
59381     },
59382     
59383     findBestRow: function(cells)
59384     {
59385         var ret = 0;
59386         
59387         for (var i =0 ; i < cells.length;i++) {
59388             ret  = Math.max(cells[i].rows || 0,ret);
59389         }
59390         return ret;
59391         
59392     },
59393     
59394     
59395     addItem : function(rec)
59396     {
59397         // look for vertical location slot in
59398         var cells = this.findCells(rec);
59399         
59400         rec.row = this.findBestRow(cells);
59401         
59402         // work out the location.
59403         
59404         var crow = false;
59405         var rows = [];
59406         for(var i =0; i < cells.length; i++) {
59407             if (!crow) {
59408                 crow = {
59409                     start : cells[i],
59410                     end :  cells[i]
59411                 };
59412                 continue;
59413             }
59414             if (crow.start.getY() == cells[i].getY()) {
59415                 // on same row.
59416                 crow.end = cells[i];
59417                 continue;
59418             }
59419             // different row.
59420             rows.push(crow);
59421             crow = {
59422                 start: cells[i],
59423                 end : cells[i]
59424             };
59425             
59426         }
59427         
59428         rows.push(crow);
59429         rec.els = [];
59430         rec.rows = rows;
59431         rec.cells = cells;
59432         for (var i = 0; i < cells.length;i++) {
59433             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59434             
59435         }
59436         
59437         
59438     },
59439     
59440     clearEvents: function() {
59441         
59442         if (!this.eventStore.getCount()) {
59443             return;
59444         }
59445         // reset number of rows in cells.
59446         Roo.each(this.cells.elements, function(c){
59447             c.rows = 0;
59448         });
59449         
59450         this.eventStore.each(function(e) {
59451             this.clearEvent(e);
59452         },this);
59453         
59454     },
59455     
59456     clearEvent : function(ev)
59457     {
59458         if (ev.els) {
59459             Roo.each(ev.els, function(el) {
59460                 el.un('mouseenter' ,this.onEventEnter, this);
59461                 el.un('mouseleave' ,this.onEventLeave, this);
59462                 el.remove();
59463             },this);
59464             ev.els = [];
59465         }
59466     },
59467     
59468     
59469     renderEvent : function(ev,ctr) {
59470         if (!ctr) {
59471              ctr = this.view.el.select('.fc-event-container',true).first();
59472         }
59473         
59474          
59475         this.clearEvent(ev);
59476             //code
59477        
59478         
59479         
59480         ev.els = [];
59481         var cells = ev.cells;
59482         var rows = ev.rows;
59483         this.fireEvent('eventrender', this, ev);
59484         
59485         for(var i =0; i < rows.length; i++) {
59486             
59487             cls = '';
59488             if (i == 0) {
59489                 cls += ' fc-event-start';
59490             }
59491             if ((i+1) == rows.length) {
59492                 cls += ' fc-event-end';
59493             }
59494             
59495             //Roo.log(ev.data);
59496             // how many rows should it span..
59497             var cg = this.eventTmpl.append(ctr,Roo.apply({
59498                 fccls : cls
59499                 
59500             }, ev.data) , true);
59501             
59502             
59503             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59504             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59505             cg.on('click', this.onEventClick, this, ev);
59506             
59507             ev.els.push(cg);
59508             
59509             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59510             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59511             //Roo.log(cg);
59512              
59513             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59514             cg.setWidth(ebox.right - sbox.x -2);
59515         }
59516     },
59517     
59518     renderEvents: function()
59519     {   
59520         // first make sure there is enough space..
59521         
59522         if (!this.eventTmpl) {
59523             this.eventTmpl = new Roo.Template(
59524                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59525                     '<div class="fc-event-inner">' +
59526                         '<span class="fc-event-time">{time}</span>' +
59527                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59528                     '</div>' +
59529                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59530                 '</div>'
59531             );
59532                 
59533         }
59534                
59535         
59536         
59537         this.cells.each(function(c) {
59538             //Roo.log(c.select('.fc-day-content div',true).first());
59539             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59540         });
59541         
59542         var ctr = this.view.el.select('.fc-event-container',true).first();
59543         
59544         var cls;
59545         this.eventStore.each(function(ev){
59546             
59547             this.renderEvent(ev);
59548              
59549              
59550         }, this);
59551         this.view.layout();
59552         
59553     },
59554     
59555     onEventEnter: function (e, el,event,d) {
59556         this.fireEvent('evententer', this, el, event);
59557     },
59558     
59559     onEventLeave: function (e, el,event,d) {
59560         this.fireEvent('eventleave', this, el, event);
59561     },
59562     
59563     onEventClick: function (e, el,event,d) {
59564         this.fireEvent('eventclick', this, el, event);
59565     },
59566     
59567     onMonthChange: function () {
59568         this.store.load();
59569     },
59570     
59571     onLoad: function () {
59572         
59573         //Roo.log('calendar onload');
59574 //         
59575         if(this.eventStore.getCount() > 0){
59576             
59577            
59578             
59579             this.eventStore.each(function(d){
59580                 
59581                 
59582                 // FIXME..
59583                 var add =   d.data;
59584                 if (typeof(add.end_dt) == 'undefined')  {
59585                     Roo.log("Missing End time in calendar data: ");
59586                     Roo.log(d);
59587                     return;
59588                 }
59589                 if (typeof(add.start_dt) == 'undefined')  {
59590                     Roo.log("Missing Start time in calendar data: ");
59591                     Roo.log(d);
59592                     return;
59593                 }
59594                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59595                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59596                 add.id = add.id || d.id;
59597                 add.title = add.title || '??';
59598                 
59599                 this.addItem(d);
59600                 
59601              
59602             },this);
59603         }
59604         
59605         this.renderEvents();
59606     }
59607     
59608
59609 });
59610 /*
59611  grid : {
59612                 xtype: 'Grid',
59613                 xns: Roo.grid,
59614                 listeners : {
59615                     render : function ()
59616                     {
59617                         _this.grid = this;
59618                         
59619                         if (!this.view.el.hasClass('course-timesheet')) {
59620                             this.view.el.addClass('course-timesheet');
59621                         }
59622                         if (this.tsStyle) {
59623                             this.ds.load({});
59624                             return; 
59625                         }
59626                         Roo.log('width');
59627                         Roo.log(_this.grid.view.el.getWidth());
59628                         
59629                         
59630                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59631                             '.course-timesheet .x-grid-row' : {
59632                                 height: '80px'
59633                             },
59634                             '.x-grid-row td' : {
59635                                 'vertical-align' : 0
59636                             },
59637                             '.course-edit-link' : {
59638                                 'color' : 'blue',
59639                                 'text-overflow' : 'ellipsis',
59640                                 'overflow' : 'hidden',
59641                                 'white-space' : 'nowrap',
59642                                 'cursor' : 'pointer'
59643                             },
59644                             '.sub-link' : {
59645                                 'color' : 'green'
59646                             },
59647                             '.de-act-sup-link' : {
59648                                 'color' : 'purple',
59649                                 'text-decoration' : 'line-through'
59650                             },
59651                             '.de-act-link' : {
59652                                 'color' : 'red',
59653                                 'text-decoration' : 'line-through'
59654                             },
59655                             '.course-timesheet .course-highlight' : {
59656                                 'border-top-style': 'dashed !important',
59657                                 'border-bottom-bottom': 'dashed !important'
59658                             },
59659                             '.course-timesheet .course-item' : {
59660                                 'font-family'   : 'tahoma, arial, helvetica',
59661                                 'font-size'     : '11px',
59662                                 'overflow'      : 'hidden',
59663                                 'padding-left'  : '10px',
59664                                 'padding-right' : '10px',
59665                                 'padding-top' : '10px' 
59666                             }
59667                             
59668                         }, Roo.id());
59669                                 this.ds.load({});
59670                     }
59671                 },
59672                 autoWidth : true,
59673                 monitorWindowResize : false,
59674                 cellrenderer : function(v,x,r)
59675                 {
59676                     return v;
59677                 },
59678                 sm : {
59679                     xtype: 'CellSelectionModel',
59680                     xns: Roo.grid
59681                 },
59682                 dataSource : {
59683                     xtype: 'Store',
59684                     xns: Roo.data,
59685                     listeners : {
59686                         beforeload : function (_self, options)
59687                         {
59688                             options.params = options.params || {};
59689                             options.params._month = _this.monthField.getValue();
59690                             options.params.limit = 9999;
59691                             options.params['sort'] = 'when_dt';    
59692                             options.params['dir'] = 'ASC';    
59693                             this.proxy.loadResponse = this.loadResponse;
59694                             Roo.log("load?");
59695                             //this.addColumns();
59696                         },
59697                         load : function (_self, records, options)
59698                         {
59699                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59700                                 // if you click on the translation.. you can edit it...
59701                                 var el = Roo.get(this);
59702                                 var id = el.dom.getAttribute('data-id');
59703                                 var d = el.dom.getAttribute('data-date');
59704                                 var t = el.dom.getAttribute('data-time');
59705                                 //var id = this.child('span').dom.textContent;
59706                                 
59707                                 //Roo.log(this);
59708                                 Pman.Dialog.CourseCalendar.show({
59709                                     id : id,
59710                                     when_d : d,
59711                                     when_t : t,
59712                                     productitem_active : id ? 1 : 0
59713                                 }, function() {
59714                                     _this.grid.ds.load({});
59715                                 });
59716                            
59717                            });
59718                            
59719                            _this.panel.fireEvent('resize', [ '', '' ]);
59720                         }
59721                     },
59722                     loadResponse : function(o, success, response){
59723                             // this is overridden on before load..
59724                             
59725                             Roo.log("our code?");       
59726                             //Roo.log(success);
59727                             //Roo.log(response)
59728                             delete this.activeRequest;
59729                             if(!success){
59730                                 this.fireEvent("loadexception", this, o, response);
59731                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59732                                 return;
59733                             }
59734                             var result;
59735                             try {
59736                                 result = o.reader.read(response);
59737                             }catch(e){
59738                                 Roo.log("load exception?");
59739                                 this.fireEvent("loadexception", this, o, response, e);
59740                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59741                                 return;
59742                             }
59743                             Roo.log("ready...");        
59744                             // loop through result.records;
59745                             // and set this.tdate[date] = [] << array of records..
59746                             _this.tdata  = {};
59747                             Roo.each(result.records, function(r){
59748                                 //Roo.log(r.data);
59749                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59750                                     _this.tdata[r.data.when_dt.format('j')] = [];
59751                                 }
59752                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59753                             });
59754                             
59755                             //Roo.log(_this.tdata);
59756                             
59757                             result.records = [];
59758                             result.totalRecords = 6;
59759                     
59760                             // let's generate some duumy records for the rows.
59761                             //var st = _this.dateField.getValue();
59762                             
59763                             // work out monday..
59764                             //st = st.add(Date.DAY, -1 * st.format('w'));
59765                             
59766                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59767                             
59768                             var firstOfMonth = date.getFirstDayOfMonth();
59769                             var days = date.getDaysInMonth();
59770                             var d = 1;
59771                             var firstAdded = false;
59772                             for (var i = 0; i < result.totalRecords ; i++) {
59773                                 //var d= st.add(Date.DAY, i);
59774                                 var row = {};
59775                                 var added = 0;
59776                                 for(var w = 0 ; w < 7 ; w++){
59777                                     if(!firstAdded && firstOfMonth != w){
59778                                         continue;
59779                                     }
59780                                     if(d > days){
59781                                         continue;
59782                                     }
59783                                     firstAdded = true;
59784                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59785                                     row['weekday'+w] = String.format(
59786                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59787                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59788                                                     d,
59789                                                     date.format('Y-m-')+dd
59790                                                 );
59791                                     added++;
59792                                     if(typeof(_this.tdata[d]) != 'undefined'){
59793                                         Roo.each(_this.tdata[d], function(r){
59794                                             var is_sub = '';
59795                                             var deactive = '';
59796                                             var id = r.id;
59797                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59798                                             if(r.parent_id*1>0){
59799                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59800                                                 id = r.parent_id;
59801                                             }
59802                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59803                                                 deactive = 'de-act-link';
59804                                             }
59805                                             
59806                                             row['weekday'+w] += String.format(
59807                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59808                                                     id, //0
59809                                                     r.product_id_name, //1
59810                                                     r.when_dt.format('h:ia'), //2
59811                                                     is_sub, //3
59812                                                     deactive, //4
59813                                                     desc // 5
59814                                             );
59815                                         });
59816                                     }
59817                                     d++;
59818                                 }
59819                                 
59820                                 // only do this if something added..
59821                                 if(added > 0){ 
59822                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59823                                 }
59824                                 
59825                                 
59826                                 // push it twice. (second one with an hour..
59827                                 
59828                             }
59829                             //Roo.log(result);
59830                             this.fireEvent("load", this, o, o.request.arg);
59831                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59832                         },
59833                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59834                     proxy : {
59835                         xtype: 'HttpProxy',
59836                         xns: Roo.data,
59837                         method : 'GET',
59838                         url : baseURL + '/Roo/Shop_course.php'
59839                     },
59840                     reader : {
59841                         xtype: 'JsonReader',
59842                         xns: Roo.data,
59843                         id : 'id',
59844                         fields : [
59845                             {
59846                                 'name': 'id',
59847                                 'type': 'int'
59848                             },
59849                             {
59850                                 'name': 'when_dt',
59851                                 'type': 'string'
59852                             },
59853                             {
59854                                 'name': 'end_dt',
59855                                 'type': 'string'
59856                             },
59857                             {
59858                                 'name': 'parent_id',
59859                                 'type': 'int'
59860                             },
59861                             {
59862                                 'name': 'product_id',
59863                                 'type': 'int'
59864                             },
59865                             {
59866                                 'name': 'productitem_id',
59867                                 'type': 'int'
59868                             },
59869                             {
59870                                 'name': 'guid',
59871                                 'type': 'int'
59872                             }
59873                         ]
59874                     }
59875                 },
59876                 toolbar : {
59877                     xtype: 'Toolbar',
59878                     xns: Roo,
59879                     items : [
59880                         {
59881                             xtype: 'Button',
59882                             xns: Roo.Toolbar,
59883                             listeners : {
59884                                 click : function (_self, e)
59885                                 {
59886                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59887                                     sd.setMonth(sd.getMonth()-1);
59888                                     _this.monthField.setValue(sd.format('Y-m-d'));
59889                                     _this.grid.ds.load({});
59890                                 }
59891                             },
59892                             text : "Back"
59893                         },
59894                         {
59895                             xtype: 'Separator',
59896                             xns: Roo.Toolbar
59897                         },
59898                         {
59899                             xtype: 'MonthField',
59900                             xns: Roo.form,
59901                             listeners : {
59902                                 render : function (_self)
59903                                 {
59904                                     _this.monthField = _self;
59905                                    // _this.monthField.set  today
59906                                 },
59907                                 select : function (combo, date)
59908                                 {
59909                                     _this.grid.ds.load({});
59910                                 }
59911                             },
59912                             value : (function() { return new Date(); })()
59913                         },
59914                         {
59915                             xtype: 'Separator',
59916                             xns: Roo.Toolbar
59917                         },
59918                         {
59919                             xtype: 'TextItem',
59920                             xns: Roo.Toolbar,
59921                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
59922                         },
59923                         {
59924                             xtype: 'Fill',
59925                             xns: Roo.Toolbar
59926                         },
59927                         {
59928                             xtype: 'Button',
59929                             xns: Roo.Toolbar,
59930                             listeners : {
59931                                 click : function (_self, e)
59932                                 {
59933                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59934                                     sd.setMonth(sd.getMonth()+1);
59935                                     _this.monthField.setValue(sd.format('Y-m-d'));
59936                                     _this.grid.ds.load({});
59937                                 }
59938                             },
59939                             text : "Next"
59940                         }
59941                     ]
59942                 },
59943                  
59944             }
59945         };
59946         
59947         *//*
59948  * Based on:
59949  * Ext JS Library 1.1.1
59950  * Copyright(c) 2006-2007, Ext JS, LLC.
59951  *
59952  * Originally Released Under LGPL - original licence link has changed is not relivant.
59953  *
59954  * Fork - LGPL
59955  * <script type="text/javascript">
59956  */
59957  
59958 /**
59959  * @class Roo.LoadMask
59960  * A simple utility class for generically masking elements while loading data.  If the element being masked has
59961  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
59962  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
59963  * element's UpdateManager load indicator and will be destroyed after the initial load.
59964  * @constructor
59965  * Create a new LoadMask
59966  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
59967  * @param {Object} config The config object
59968  */
59969 Roo.LoadMask = function(el, config){
59970     this.el = Roo.get(el);
59971     Roo.apply(this, config);
59972     if(this.store){
59973         this.store.on('beforeload', this.onBeforeLoad, this);
59974         this.store.on('load', this.onLoad, this);
59975         this.store.on('loadexception', this.onLoadException, this);
59976         this.removeMask = false;
59977     }else{
59978         var um = this.el.getUpdateManager();
59979         um.showLoadIndicator = false; // disable the default indicator
59980         um.on('beforeupdate', this.onBeforeLoad, this);
59981         um.on('update', this.onLoad, this);
59982         um.on('failure', this.onLoad, this);
59983         this.removeMask = true;
59984     }
59985 };
59986
59987 Roo.LoadMask.prototype = {
59988     /**
59989      * @cfg {Boolean} removeMask
59990      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
59991      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
59992      */
59993     /**
59994      * @cfg {String} msg
59995      * The text to display in a centered loading message box (defaults to 'Loading...')
59996      */
59997     msg : 'Loading...',
59998     /**
59999      * @cfg {String} msgCls
60000      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60001      */
60002     msgCls : 'x-mask-loading',
60003
60004     /**
60005      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60006      * @type Boolean
60007      */
60008     disabled: false,
60009
60010     /**
60011      * Disables the mask to prevent it from being displayed
60012      */
60013     disable : function(){
60014        this.disabled = true;
60015     },
60016
60017     /**
60018      * Enables the mask so that it can be displayed
60019      */
60020     enable : function(){
60021         this.disabled = false;
60022     },
60023     
60024     onLoadException : function()
60025     {
60026         Roo.log(arguments);
60027         
60028         if (typeof(arguments[3]) != 'undefined') {
60029             Roo.MessageBox.alert("Error loading",arguments[3]);
60030         } 
60031         /*
60032         try {
60033             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60034                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60035             }   
60036         } catch(e) {
60037             
60038         }
60039         */
60040     
60041         
60042         
60043         this.el.unmask(this.removeMask);
60044     },
60045     // private
60046     onLoad : function()
60047     {
60048         this.el.unmask(this.removeMask);
60049     },
60050
60051     // private
60052     onBeforeLoad : function(){
60053         if(!this.disabled){
60054             this.el.mask(this.msg, this.msgCls);
60055         }
60056     },
60057
60058     // private
60059     destroy : function(){
60060         if(this.store){
60061             this.store.un('beforeload', this.onBeforeLoad, this);
60062             this.store.un('load', this.onLoad, this);
60063             this.store.un('loadexception', this.onLoadException, this);
60064         }else{
60065             var um = this.el.getUpdateManager();
60066             um.un('beforeupdate', this.onBeforeLoad, this);
60067             um.un('update', this.onLoad, this);
60068             um.un('failure', this.onLoad, this);
60069         }
60070     }
60071 };/*
60072  * Based on:
60073  * Ext JS Library 1.1.1
60074  * Copyright(c) 2006-2007, Ext JS, LLC.
60075  *
60076  * Originally Released Under LGPL - original licence link has changed is not relivant.
60077  *
60078  * Fork - LGPL
60079  * <script type="text/javascript">
60080  */
60081
60082
60083 /**
60084  * @class Roo.XTemplate
60085  * @extends Roo.Template
60086  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60087 <pre><code>
60088 var t = new Roo.XTemplate(
60089         '&lt;select name="{name}"&gt;',
60090                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60091         '&lt;/select&gt;'
60092 );
60093  
60094 // then append, applying the master template values
60095  </code></pre>
60096  *
60097  * Supported features:
60098  *
60099  *  Tags:
60100
60101 <pre><code>
60102       {a_variable} - output encoded.
60103       {a_variable.format:("Y-m-d")} - call a method on the variable
60104       {a_variable:raw} - unencoded output
60105       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60106       {a_variable:this.method_on_template(...)} - call a method on the template object.
60107  
60108 </code></pre>
60109  *  The tpl tag:
60110 <pre><code>
60111         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60112         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60113         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60114         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60115   
60116         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60117         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60118 </code></pre>
60119  *      
60120  */
60121 Roo.XTemplate = function()
60122 {
60123     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60124     if (this.html) {
60125         this.compile();
60126     }
60127 };
60128
60129
60130 Roo.extend(Roo.XTemplate, Roo.Template, {
60131
60132     /**
60133      * The various sub templates
60134      */
60135     tpls : false,
60136     /**
60137      *
60138      * basic tag replacing syntax
60139      * WORD:WORD()
60140      *
60141      * // you can fake an object call by doing this
60142      *  x.t:(test,tesT) 
60143      * 
60144      */
60145     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60146
60147     /**
60148      * compile the template
60149      *
60150      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60151      *
60152      */
60153     compile: function()
60154     {
60155         var s = this.html;
60156      
60157         s = ['<tpl>', s, '</tpl>'].join('');
60158     
60159         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60160             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60161             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60162             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60163             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60164             m,
60165             id     = 0,
60166             tpls   = [];
60167     
60168         while(true == !!(m = s.match(re))){
60169             var forMatch   = m[0].match(nameRe),
60170                 ifMatch   = m[0].match(ifRe),
60171                 execMatch   = m[0].match(execRe),
60172                 namedMatch   = m[0].match(namedRe),
60173                 
60174                 exp  = null, 
60175                 fn   = null,
60176                 exec = null,
60177                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60178                 
60179             if (ifMatch) {
60180                 // if - puts fn into test..
60181                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60182                 if(exp){
60183                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60184                 }
60185             }
60186             
60187             if (execMatch) {
60188                 // exec - calls a function... returns empty if true is  returned.
60189                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60190                 if(exp){
60191                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60192                 }
60193             }
60194             
60195             
60196             if (name) {
60197                 // for = 
60198                 switch(name){
60199                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60200                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60201                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60202                 }
60203             }
60204             var uid = namedMatch ? namedMatch[1] : id;
60205             
60206             
60207             tpls.push({
60208                 id:     namedMatch ? namedMatch[1] : id,
60209                 target: name,
60210                 exec:   exec,
60211                 test:   fn,
60212                 body:   m[1] || ''
60213             });
60214             if (namedMatch) {
60215                 s = s.replace(m[0], '');
60216             } else { 
60217                 s = s.replace(m[0], '{xtpl'+ id + '}');
60218             }
60219             ++id;
60220         }
60221         this.tpls = [];
60222         for(var i = tpls.length-1; i >= 0; --i){
60223             this.compileTpl(tpls[i]);
60224             this.tpls[tpls[i].id] = tpls[i];
60225         }
60226         this.master = tpls[tpls.length-1];
60227         return this;
60228     },
60229     /**
60230      * same as applyTemplate, except it's done to one of the subTemplates
60231      * when using named templates, you can do:
60232      *
60233      * var str = pl.applySubTemplate('your-name', values);
60234      *
60235      * 
60236      * @param {Number} id of the template
60237      * @param {Object} values to apply to template
60238      * @param {Object} parent (normaly the instance of this object)
60239      */
60240     applySubTemplate : function(id, values, parent)
60241     {
60242         
60243         
60244         var t = this.tpls[id];
60245         
60246         
60247         try { 
60248             if(t.test && !t.test.call(this, values, parent)){
60249                 return '';
60250             }
60251         } catch(e) {
60252             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60253             Roo.log(e.toString());
60254             Roo.log(t.test);
60255             return ''
60256         }
60257         try { 
60258             
60259             if(t.exec && t.exec.call(this, values, parent)){
60260                 return '';
60261             }
60262         } catch(e) {
60263             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60264             Roo.log(e.toString());
60265             Roo.log(t.exec);
60266             return ''
60267         }
60268         try {
60269             var vs = t.target ? t.target.call(this, values, parent) : values;
60270             parent = t.target ? values : parent;
60271             if(t.target && vs instanceof Array){
60272                 var buf = [];
60273                 for(var i = 0, len = vs.length; i < len; i++){
60274                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60275                 }
60276                 return buf.join('');
60277             }
60278             return t.compiled.call(this, vs, parent);
60279         } catch (e) {
60280             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60281             Roo.log(e.toString());
60282             Roo.log(t.compiled);
60283             return '';
60284         }
60285     },
60286
60287     compileTpl : function(tpl)
60288     {
60289         var fm = Roo.util.Format;
60290         var useF = this.disableFormats !== true;
60291         var sep = Roo.isGecko ? "+" : ",";
60292         var undef = function(str) {
60293             Roo.log("Property not found :"  + str);
60294             return '';
60295         };
60296         
60297         var fn = function(m, name, format, args)
60298         {
60299             //Roo.log(arguments);
60300             args = args ? args.replace(/\\'/g,"'") : args;
60301             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60302             if (typeof(format) == 'undefined') {
60303                 format= 'htmlEncode';
60304             }
60305             if (format == 'raw' ) {
60306                 format = false;
60307             }
60308             
60309             if(name.substr(0, 4) == 'xtpl'){
60310                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60311             }
60312             
60313             // build an array of options to determine if value is undefined..
60314             
60315             // basically get 'xxxx.yyyy' then do
60316             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60317             //    (function () { Roo.log("Property not found"); return ''; })() :
60318             //    ......
60319             
60320             var udef_ar = [];
60321             var lookfor = '';
60322             Roo.each(name.split('.'), function(st) {
60323                 lookfor += (lookfor.length ? '.': '') + st;
60324                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60325             });
60326             
60327             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60328             
60329             
60330             if(format && useF){
60331                 
60332                 args = args ? ',' + args : "";
60333                  
60334                 if(format.substr(0, 5) != "this."){
60335                     format = "fm." + format + '(';
60336                 }else{
60337                     format = 'this.call("'+ format.substr(5) + '", ';
60338                     args = ", values";
60339                 }
60340                 
60341                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60342             }
60343              
60344             if (args.length) {
60345                 // called with xxyx.yuu:(test,test)
60346                 // change to ()
60347                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60348             }
60349             // raw.. - :raw modifier..
60350             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60351             
60352         };
60353         var body;
60354         // branched to use + in gecko and [].join() in others
60355         if(Roo.isGecko){
60356             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60357                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60358                     "';};};";
60359         }else{
60360             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60361             body.push(tpl.body.replace(/(\r\n|\n)/g,
60362                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60363             body.push("'].join('');};};");
60364             body = body.join('');
60365         }
60366         
60367         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60368        
60369         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60370         eval(body);
60371         
60372         return this;
60373     },
60374
60375     applyTemplate : function(values){
60376         return this.master.compiled.call(this, values, {});
60377         //var s = this.subs;
60378     },
60379
60380     apply : function(){
60381         return this.applyTemplate.apply(this, arguments);
60382     }
60383
60384  });
60385
60386 Roo.XTemplate.from = function(el){
60387     el = Roo.getDom(el);
60388     return new Roo.XTemplate(el.value || el.innerHTML);
60389 };