e4e250dbf97456e34db287e4ca6d14b30f1720b9
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {
69                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
70                     window.addEventListener('touchstart', function __set_has_touch__ () {
71                         Roo.isTouch = true;
72                         window.removeEventListener('touchstart', __set_has_touch__);
73                     });
74                     return false; // no touch on chrome!?
75                 }
76                 document.createEvent("TouchEvent");  
77                 return true;  
78             } catch (e) {  
79                 return false;  
80             } 
81             
82         })();
83     // remove css image flicker
84         if(isIE && !isIE7){
85         try{
86             document.execCommand("BackgroundImageCache", false, true);
87         }catch(e){}
88     }
89     
90     Roo.apply(Roo, {
91         /**
92          * True if the browser is in strict mode
93          * @type Boolean
94          */
95         isStrict : isStrict,
96         /**
97          * True if the page is running over SSL
98          * @type Boolean
99          */
100         isSecure : isSecure,
101         /**
102          * True when the document is fully initialized and ready for action
103          * @type Boolean
104          */
105         isReady : false,
106         /**
107          * Turn on debugging output (currently only the factory uses this)
108          * @type Boolean
109          */
110         
111         debug: false,
112
113         /**
114          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
115          * @type Boolean
116          */
117         enableGarbageCollector : true,
118
119         /**
120          * True to automatically purge event listeners after uncaching an element (defaults to false).
121          * Note: this only happens if enableGarbageCollector is true.
122          * @type Boolean
123          */
124         enableListenerCollection:false,
125
126         /**
127          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
128          * the IE insecure content warning (defaults to javascript:false).
129          * @type String
130          */
131         SSL_SECURE_URL : "javascript:false",
132
133         /**
134          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
135          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
136          * @type String
137          */
138         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
139
140         emptyFn : function(){},
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isFirefox : isFirefox,
624         /** @type Boolean */
625         isIE : isIE,
626         /** @type Boolean */
627         isIE7 : isIE7,
628         /** @type Boolean */
629         isIE11 : isIE11,
630         /** @type Boolean */
631         isGecko : isGecko,
632         /** @type Boolean */
633         isBorderBox : isBorderBox,
634         /** @type Boolean */
635         isWindows : isWindows,
636         /** @type Boolean */
637         isLinux : isLinux,
638         /** @type Boolean */
639         isMac : isMac,
640         /** @type Boolean */
641         isIOS : isIOS,
642         /** @type Boolean */
643         isTouch : isTouch,
644
645         /**
646          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
647          * you may want to set this to true.
648          * @type Boolean
649          */
650         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
651         
652         
653                 
654         /**
655          * Selects a single element as a Roo Element
656          * This is about as close as you can get to jQuery's $('do crazy stuff')
657          * @param {String} selector The selector/xpath query
658          * @param {Node} root (optional) The start of the query (defaults to document).
659          * @return {Roo.Element}
660          */
661         selectNode : function(selector, root) 
662         {
663             var node = Roo.DomQuery.selectNode(selector,root);
664             return node ? Roo.get(node) : new Roo.Element(false);
665         }
666         
667     });
668
669
670 })();
671
672 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
673                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
674                 "Roo.app", "Roo.ux",
675                 "Roo.bootstrap",
676                 "Roo.bootstrap.dash");
677 /*
678  * Based on:
679  * Ext JS Library 1.1.1
680  * Copyright(c) 2006-2007, Ext JS, LLC.
681  *
682  * Originally Released Under LGPL - original licence link has changed is not relivant.
683  *
684  * Fork - LGPL
685  * <script type="text/javascript">
686  */
687
688 (function() {    
689     // wrappedn so fnCleanup is not in global scope...
690     if(Roo.isIE) {
691         function fnCleanUp() {
692             var p = Function.prototype;
693             delete p.createSequence;
694             delete p.defer;
695             delete p.createDelegate;
696             delete p.createCallback;
697             delete p.createInterceptor;
698
699             window.detachEvent("onunload", fnCleanUp);
700         }
701         window.attachEvent("onunload", fnCleanUp);
702     }
703 })();
704
705
706 /**
707  * @class Function
708  * These functions are available on every Function object (any JavaScript function).
709  */
710 Roo.apply(Function.prototype, {
711      /**
712      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
713      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
714      * Will create a function that is bound to those 2 args.
715      * @return {Function} The new function
716     */
717     createCallback : function(/*args...*/){
718         // make args available, in function below
719         var args = arguments;
720         var method = this;
721         return function() {
722             return method.apply(window, args);
723         };
724     },
725
726     /**
727      * Creates a delegate (callback) that sets the scope to obj.
728      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
729      * Will create a function that is automatically scoped to this.
730      * @param {Object} obj (optional) The object for which the scope is set
731      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
732      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
733      *                                             if a number the args are inserted at the specified position
734      * @return {Function} The new function
735      */
736     createDelegate : function(obj, args, appendArgs){
737         var method = this;
738         return function() {
739             var callArgs = args || arguments;
740             if(appendArgs === true){
741                 callArgs = Array.prototype.slice.call(arguments, 0);
742                 callArgs = callArgs.concat(args);
743             }else if(typeof appendArgs == "number"){
744                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
745                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
746                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
747             }
748             return method.apply(obj || window, callArgs);
749         };
750     },
751
752     /**
753      * Calls this function after the number of millseconds specified.
754      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
755      * @param {Object} obj (optional) The object for which the scope is set
756      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
757      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
758      *                                             if a number the args are inserted at the specified position
759      * @return {Number} The timeout id that can be used with clearTimeout
760      */
761     defer : function(millis, obj, args, appendArgs){
762         var fn = this.createDelegate(obj, args, appendArgs);
763         if(millis){
764             return setTimeout(fn, millis);
765         }
766         fn();
767         return 0;
768     },
769     /**
770      * Create a combined function call sequence of the original function + the passed function.
771      * The resulting function returns the results of the original function.
772      * The passed fcn is called with the parameters of the original function
773      * @param {Function} fcn The function to sequence
774      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
775      * @return {Function} The new function
776      */
777     createSequence : function(fcn, scope){
778         if(typeof fcn != "function"){
779             return this;
780         }
781         var method = this;
782         return function() {
783             var retval = method.apply(this || window, arguments);
784             fcn.apply(scope || this || window, arguments);
785             return retval;
786         };
787     },
788
789     /**
790      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
791      * The resulting function returns the results of the original function.
792      * The passed fcn is called with the parameters of the original function.
793      * @addon
794      * @param {Function} fcn The function to call before the original
795      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
796      * @return {Function} The new function
797      */
798     createInterceptor : function(fcn, scope){
799         if(typeof fcn != "function"){
800             return this;
801         }
802         var method = this;
803         return function() {
804             fcn.target = this;
805             fcn.method = method;
806             if(fcn.apply(scope || this || window, arguments) === false){
807                 return;
808             }
809             return method.apply(this || window, arguments);
810         };
811     }
812 });
813 /*
814  * Based on:
815  * Ext JS Library 1.1.1
816  * Copyright(c) 2006-2007, Ext JS, LLC.
817  *
818  * Originally Released Under LGPL - original licence link has changed is not relivant.
819  *
820  * Fork - LGPL
821  * <script type="text/javascript">
822  */
823
824 Roo.applyIf(String, {
825     
826     /** @scope String */
827     
828     /**
829      * Escapes the passed string for ' and \
830      * @param {String} string The string to escape
831      * @return {String} The escaped string
832      * @static
833      */
834     escape : function(string) {
835         return string.replace(/('|\\)/g, "\\$1");
836     },
837
838     /**
839      * Pads the left side of a string with a specified character.  This is especially useful
840      * for normalizing number and date strings.  Example usage:
841      * <pre><code>
842 var s = String.leftPad('123', 5, '0');
843 // s now contains the string: '00123'
844 </code></pre>
845      * @param {String} string The original string
846      * @param {Number} size The total length of the output string
847      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
848      * @return {String} The padded string
849      * @static
850      */
851     leftPad : function (val, size, ch) {
852         var result = new String(val);
853         if(ch === null || ch === undefined || ch === '') {
854             ch = " ";
855         }
856         while (result.length < size) {
857             result = ch + result;
858         }
859         return result;
860     },
861
862     /**
863      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
864      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
865      * <pre><code>
866 var cls = 'my-class', text = 'Some text';
867 var s = String.format('<div class="{0}">{1}</div>', cls, text);
868 // s now contains the string: '<div class="my-class">Some text</div>'
869 </code></pre>
870      * @param {String} string The tokenized string to be formatted
871      * @param {String} value1 The value to replace token {0}
872      * @param {String} value2 Etc...
873      * @return {String} The formatted string
874      * @static
875      */
876     format : function(format){
877         var args = Array.prototype.slice.call(arguments, 1);
878         return format.replace(/\{(\d+)\}/g, function(m, i){
879             return Roo.util.Format.htmlEncode(args[i]);
880         });
881     }
882 });
883
884 /**
885  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
886  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
887  * they are already different, the first value passed in is returned.  Note that this method returns the new value
888  * but does not change the current string.
889  * <pre><code>
890 // alternate sort directions
891 sort = sort.toggle('ASC', 'DESC');
892
893 // instead of conditional logic:
894 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
895 </code></pre>
896  * @param {String} value The value to compare to the current string
897  * @param {String} other The new value to use if the string already equals the first value passed in
898  * @return {String} The new value
899  */
900  
901 String.prototype.toggle = function(value, other){
902     return this == value ? other : value;
903 };/*
904  * Based on:
905  * Ext JS Library 1.1.1
906  * Copyright(c) 2006-2007, Ext JS, LLC.
907  *
908  * Originally Released Under LGPL - original licence link has changed is not relivant.
909  *
910  * Fork - LGPL
911  * <script type="text/javascript">
912  */
913
914  /**
915  * @class Number
916  */
917 Roo.applyIf(Number.prototype, {
918     /**
919      * Checks whether or not the current number is within a desired range.  If the number is already within the
920      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
921      * exceeded.  Note that this method returns the constrained value but does not change the current number.
922      * @param {Number} min The minimum number in the range
923      * @param {Number} max The maximum number in the range
924      * @return {Number} The constrained value if outside the range, otherwise the current value
925      */
926     constrain : function(min, max){
927         return Math.min(Math.max(this, min), max);
928     }
929 });/*
930  * Based on:
931  * Ext JS Library 1.1.1
932  * Copyright(c) 2006-2007, Ext JS, LLC.
933  *
934  * Originally Released Under LGPL - original licence link has changed is not relivant.
935  *
936  * Fork - LGPL
937  * <script type="text/javascript">
938  */
939  /**
940  * @class Array
941  */
942 Roo.applyIf(Array.prototype, {
943     /**
944      * 
945      * Checks whether or not the specified object exists in the array.
946      * @param {Object} o The object to check for
947      * @return {Number} The index of o in the array (or -1 if it is not found)
948      */
949     indexOf : function(o){
950        for (var i = 0, len = this.length; i < len; i++){
951               if(this[i] == o) { return i; }
952        }
953            return -1;
954     },
955
956     /**
957      * Removes the specified object from the array.  If the object is not found nothing happens.
958      * @param {Object} o The object to remove
959      */
960     remove : function(o){
961        var index = this.indexOf(o);
962        if(index != -1){
963            this.splice(index, 1);
964        }
965     },
966     /**
967      * Map (JS 1.6 compatibility)
968      * @param {Function} function  to call
969      */
970     map : function(fun )
971     {
972         var len = this.length >>> 0;
973         if (typeof fun != "function") {
974             throw new TypeError();
975         }
976         var res = new Array(len);
977         var thisp = arguments[1];
978         for (var i = 0; i < len; i++)
979         {
980             if (i in this) {
981                 res[i] = fun.call(thisp, this[i], i, this);
982             }
983         }
984
985         return res;
986     }
987     
988 });
989
990
991  
992 /*
993  * Based on:
994  * Ext JS Library 1.1.1
995  * Copyright(c) 2006-2007, Ext JS, LLC.
996  *
997  * Originally Released Under LGPL - original licence link has changed is not relivant.
998  *
999  * Fork - LGPL
1000  * <script type="text/javascript">
1001  */
1002
1003 /**
1004  * @class Date
1005  *
1006  * The date parsing and format syntax is a subset of
1007  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1008  * supported will provide results equivalent to their PHP versions.
1009  *
1010  * Following is the list of all currently supported formats:
1011  *<pre>
1012 Sample date:
1013 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1014
1015 Format  Output      Description
1016 ------  ----------  --------------------------------------------------------------
1017   d      10         Day of the month, 2 digits with leading zeros
1018   D      Wed        A textual representation of a day, three letters
1019   j      10         Day of the month without leading zeros
1020   l      Wednesday  A full textual representation of the day of the week
1021   S      th         English ordinal day of month suffix, 2 chars (use with j)
1022   w      3          Numeric representation of the day of the week
1023   z      9          The julian date, or day of the year (0-365)
1024   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1025   F      January    A full textual representation of the month
1026   m      01         Numeric representation of a month, with leading zeros
1027   M      Jan        Month name abbreviation, three letters
1028   n      1          Numeric representation of a month, without leading zeros
1029   t      31         Number of days in the given month
1030   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1031   Y      2007       A full numeric representation of a year, 4 digits
1032   y      07         A two digit representation of a year
1033   a      pm         Lowercase Ante meridiem and Post meridiem
1034   A      PM         Uppercase Ante meridiem and Post meridiem
1035   g      3          12-hour format of an hour without leading zeros
1036   G      15         24-hour format of an hour without leading zeros
1037   h      03         12-hour format of an hour with leading zeros
1038   H      15         24-hour format of an hour with leading zeros
1039   i      05         Minutes with leading zeros
1040   s      01         Seconds, with leading zeros
1041   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1042   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1043   T      CST        Timezone setting of the machine running the code
1044   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1045 </pre>
1046  *
1047  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1048  * <pre><code>
1049 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1050 document.write(dt.format('Y-m-d'));                         //2007-01-10
1051 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1052 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1053  </code></pre>
1054  *
1055  * Here are some standard date/time patterns that you might find helpful.  They
1056  * are not part of the source of Date.js, but to use them you can simply copy this
1057  * block of code into any script that is included after Date.js and they will also become
1058  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1059  * <pre><code>
1060 Date.patterns = {
1061     ISO8601Long:"Y-m-d H:i:s",
1062     ISO8601Short:"Y-m-d",
1063     ShortDate: "n/j/Y",
1064     LongDate: "l, F d, Y",
1065     FullDateTime: "l, F d, Y g:i:s A",
1066     MonthDay: "F d",
1067     ShortTime: "g:i A",
1068     LongTime: "g:i:s A",
1069     SortableDateTime: "Y-m-d\\TH:i:s",
1070     UniversalSortableDateTime: "Y-m-d H:i:sO",
1071     YearMonth: "F, Y"
1072 };
1073 </code></pre>
1074  *
1075  * Example usage:
1076  * <pre><code>
1077 var dt = new Date();
1078 document.write(dt.format(Date.patterns.ShortDate));
1079  </code></pre>
1080  */
1081
1082 /*
1083  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1084  * They generate precompiled functions from date formats instead of parsing and
1085  * processing the pattern every time you format a date.  These functions are available
1086  * on every Date object (any javascript function).
1087  *
1088  * The original article and download are here:
1089  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1090  *
1091  */
1092  
1093  
1094  // was in core
1095 /**
1096  Returns the number of milliseconds between this date and date
1097  @param {Date} date (optional) Defaults to now
1098  @return {Number} The diff in milliseconds
1099  @member Date getElapsed
1100  */
1101 Date.prototype.getElapsed = function(date) {
1102         return Math.abs((date || new Date()).getTime()-this.getTime());
1103 };
1104 // was in date file..
1105
1106
1107 // private
1108 Date.parseFunctions = {count:0};
1109 // private
1110 Date.parseRegexes = [];
1111 // private
1112 Date.formatFunctions = {count:0};
1113
1114 // private
1115 Date.prototype.dateFormat = function(format) {
1116     if (Date.formatFunctions[format] == null) {
1117         Date.createNewFormat(format);
1118     }
1119     var func = Date.formatFunctions[format];
1120     return this[func]();
1121 };
1122
1123
1124 /**
1125  * Formats a date given the supplied format string
1126  * @param {String} format The format string
1127  * @return {String} The formatted date
1128  * @method
1129  */
1130 Date.prototype.format = Date.prototype.dateFormat;
1131
1132 // private
1133 Date.createNewFormat = function(format) {
1134     var funcName = "format" + Date.formatFunctions.count++;
1135     Date.formatFunctions[format] = funcName;
1136     var code = "Date.prototype." + funcName + " = function(){return ";
1137     var special = false;
1138     var ch = '';
1139     for (var i = 0; i < format.length; ++i) {
1140         ch = format.charAt(i);
1141         if (!special && ch == "\\") {
1142             special = true;
1143         }
1144         else if (special) {
1145             special = false;
1146             code += "'" + String.escape(ch) + "' + ";
1147         }
1148         else {
1149             code += Date.getFormatCode(ch);
1150         }
1151     }
1152     /** eval:var:zzzzzzzzzzzzz */
1153     eval(code.substring(0, code.length - 3) + ";}");
1154 };
1155
1156 // private
1157 Date.getFormatCode = function(character) {
1158     switch (character) {
1159     case "d":
1160         return "String.leftPad(this.getDate(), 2, '0') + ";
1161     case "D":
1162         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1163     case "j":
1164         return "this.getDate() + ";
1165     case "l":
1166         return "Date.dayNames[this.getDay()] + ";
1167     case "S":
1168         return "this.getSuffix() + ";
1169     case "w":
1170         return "this.getDay() + ";
1171     case "z":
1172         return "this.getDayOfYear() + ";
1173     case "W":
1174         return "this.getWeekOfYear() + ";
1175     case "F":
1176         return "Date.monthNames[this.getMonth()] + ";
1177     case "m":
1178         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1179     case "M":
1180         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1181     case "n":
1182         return "(this.getMonth() + 1) + ";
1183     case "t":
1184         return "this.getDaysInMonth() + ";
1185     case "L":
1186         return "(this.isLeapYear() ? 1 : 0) + ";
1187     case "Y":
1188         return "this.getFullYear() + ";
1189     case "y":
1190         return "('' + this.getFullYear()).substring(2, 4) + ";
1191     case "a":
1192         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1193     case "A":
1194         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1195     case "g":
1196         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1197     case "G":
1198         return "this.getHours() + ";
1199     case "h":
1200         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1201     case "H":
1202         return "String.leftPad(this.getHours(), 2, '0') + ";
1203     case "i":
1204         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1205     case "s":
1206         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1207     case "O":
1208         return "this.getGMTOffset() + ";
1209     case "P":
1210         return "this.getGMTColonOffset() + ";
1211     case "T":
1212         return "this.getTimezone() + ";
1213     case "Z":
1214         return "(this.getTimezoneOffset() * -60) + ";
1215     default:
1216         return "'" + String.escape(character) + "' + ";
1217     }
1218 };
1219
1220 /**
1221  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1222  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1223  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1224  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1225  * string or the parse operation will fail.
1226  * Example Usage:
1227 <pre><code>
1228 //dt = Fri May 25 2007 (current date)
1229 var dt = new Date();
1230
1231 //dt = Thu May 25 2006 (today's month/day in 2006)
1232 dt = Date.parseDate("2006", "Y");
1233
1234 //dt = Sun Jan 15 2006 (all date parts specified)
1235 dt = Date.parseDate("2006-1-15", "Y-m-d");
1236
1237 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1238 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1239 </code></pre>
1240  * @param {String} input The unparsed date as a string
1241  * @param {String} format The format the date is in
1242  * @return {Date} The parsed date
1243  * @static
1244  */
1245 Date.parseDate = function(input, format) {
1246     if (Date.parseFunctions[format] == null) {
1247         Date.createParser(format);
1248     }
1249     var func = Date.parseFunctions[format];
1250     return Date[func](input);
1251 };
1252 /**
1253  * @private
1254  */
1255
1256 Date.createParser = function(format) {
1257     var funcName = "parse" + Date.parseFunctions.count++;
1258     var regexNum = Date.parseRegexes.length;
1259     var currentGroup = 1;
1260     Date.parseFunctions[format] = funcName;
1261
1262     var code = "Date." + funcName + " = function(input){\n"
1263         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1264         + "var d = new Date();\n"
1265         + "y = d.getFullYear();\n"
1266         + "m = d.getMonth();\n"
1267         + "d = d.getDate();\n"
1268         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1269         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1270         + "if (results && results.length > 0) {";
1271     var regex = "";
1272
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             regex += String.escape(ch);
1283         }
1284         else {
1285             var obj = Date.formatCodeToRegex(ch, currentGroup);
1286             currentGroup += obj.g;
1287             regex += obj.s;
1288             if (obj.g && obj.c) {
1289                 code += obj.c;
1290             }
1291         }
1292     }
1293
1294     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1295         + "{v = new Date(y, m, d, h, i, s);}\n"
1296         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1297         + "{v = new Date(y, m, d, h, i);}\n"
1298         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1299         + "{v = new Date(y, m, d, h);}\n"
1300         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1301         + "{v = new Date(y, m, d);}\n"
1302         + "else if (y >= 0 && m >= 0)\n"
1303         + "{v = new Date(y, m);}\n"
1304         + "else if (y >= 0)\n"
1305         + "{v = new Date(y);}\n"
1306         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1307         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1308         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1309         + ";}";
1310
1311     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1312     /** eval:var:zzzzzzzzzzzzz */
1313     eval(code);
1314 };
1315
1316 // private
1317 Date.formatCodeToRegex = function(character, currentGroup) {
1318     switch (character) {
1319     case "D":
1320         return {g:0,
1321         c:null,
1322         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1323     case "j":
1324         return {g:1,
1325             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1326             s:"(\\d{1,2})"}; // day of month without leading zeroes
1327     case "d":
1328         return {g:1,
1329             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1330             s:"(\\d{2})"}; // day of month with leading zeroes
1331     case "l":
1332         return {g:0,
1333             c:null,
1334             s:"(?:" + Date.dayNames.join("|") + ")"};
1335     case "S":
1336         return {g:0,
1337             c:null,
1338             s:"(?:st|nd|rd|th)"};
1339     case "w":
1340         return {g:0,
1341             c:null,
1342             s:"\\d"};
1343     case "z":
1344         return {g:0,
1345             c:null,
1346             s:"(?:\\d{1,3})"};
1347     case "W":
1348         return {g:0,
1349             c:null,
1350             s:"(?:\\d{2})"};
1351     case "F":
1352         return {g:1,
1353             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1354             s:"(" + Date.monthNames.join("|") + ")"};
1355     case "M":
1356         return {g:1,
1357             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1358             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1359     case "n":
1360         return {g:1,
1361             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1362             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1363     case "m":
1364         return {g:1,
1365             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1366             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1367     case "t":
1368         return {g:0,
1369             c:null,
1370             s:"\\d{1,2}"};
1371     case "L":
1372         return {g:0,
1373             c:null,
1374             s:"(?:1|0)"};
1375     case "Y":
1376         return {g:1,
1377             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1378             s:"(\\d{4})"};
1379     case "y":
1380         return {g:1,
1381             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1382                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1383             s:"(\\d{1,2})"};
1384     case "a":
1385         return {g:1,
1386             c:"if (results[" + currentGroup + "] == 'am') {\n"
1387                 + "if (h == 12) { h = 0; }\n"
1388                 + "} else { if (h < 12) { h += 12; }}",
1389             s:"(am|pm)"};
1390     case "A":
1391         return {g:1,
1392             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1393                 + "if (h == 12) { h = 0; }\n"
1394                 + "} else { if (h < 12) { h += 12; }}",
1395             s:"(AM|PM)"};
1396     case "g":
1397     case "G":
1398         return {g:1,
1399             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1401     case "h":
1402     case "H":
1403         return {g:1,
1404             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1405             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1406     case "i":
1407         return {g:1,
1408             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1409             s:"(\\d{2})"};
1410     case "s":
1411         return {g:1,
1412             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1413             s:"(\\d{2})"};
1414     case "O":
1415         return {g:1,
1416             c:[
1417                 "o = results[", currentGroup, "];\n",
1418                 "var sn = o.substring(0,1);\n", // get + / - sign
1419                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1420                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1421                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1422                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1423             ].join(""),
1424             s:"([+\-]\\d{2,4})"};
1425     
1426     
1427     case "P":
1428         return {g:1,
1429                 c:[
1430                    "o = results[", currentGroup, "];\n",
1431                    "var sn = o.substring(0,1);\n",
1432                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1433                    "var mn = o.substring(4,6) % 60;\n",
1434                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1435                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1436             ].join(""),
1437             s:"([+\-]\\d{4})"};
1438     case "T":
1439         return {g:0,
1440             c:null,
1441             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1442     case "Z":
1443         return {g:1,
1444             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1445                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1446             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1447     default:
1448         return {g:0,
1449             c:null,
1450             s:String.escape(character)};
1451     }
1452 };
1453
1454 /**
1455  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1456  * @return {String} The abbreviated timezone name (e.g. 'CST')
1457  */
1458 Date.prototype.getTimezone = function() {
1459     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1460 };
1461
1462 /**
1463  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1464  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1465  */
1466 Date.prototype.getGMTOffset = function() {
1467     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1468         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1469         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1470 };
1471
1472 /**
1473  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1474  * @return {String} 2-characters representing hours and 2-characters representing minutes
1475  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1476  */
1477 Date.prototype.getGMTColonOffset = function() {
1478         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1479                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1480                 + ":"
1481                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1482 }
1483
1484 /**
1485  * Get the numeric day number of the year, adjusted for leap year.
1486  * @return {Number} 0 through 364 (365 in leap years)
1487  */
1488 Date.prototype.getDayOfYear = function() {
1489     var num = 0;
1490     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1491     for (var i = 0; i < this.getMonth(); ++i) {
1492         num += Date.daysInMonth[i];
1493     }
1494     return num + this.getDate() - 1;
1495 };
1496
1497 /**
1498  * Get the string representation of the numeric week number of the year
1499  * (equivalent to the format specifier 'W').
1500  * @return {String} '00' through '52'
1501  */
1502 Date.prototype.getWeekOfYear = function() {
1503     // Skip to Thursday of this week
1504     var now = this.getDayOfYear() + (4 - this.getDay());
1505     // Find the first Thursday of the year
1506     var jan1 = new Date(this.getFullYear(), 0, 1);
1507     var then = (7 - jan1.getDay() + 4);
1508     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1509 };
1510
1511 /**
1512  * Whether or not the current date is in a leap year.
1513  * @return {Boolean} True if the current date is in a leap year, else false
1514  */
1515 Date.prototype.isLeapYear = function() {
1516     var year = this.getFullYear();
1517     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1518 };
1519
1520 /**
1521  * Get the first day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getFirstDayOfMonth = function() {
1532     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536 /**
1537  * Get the last day of the current month, adjusted for leap year.  The returned value
1538  * is the numeric day index within the week (0-6) which can be used in conjunction with
1539  * the {@link #monthNames} array to retrieve the textual day name.
1540  * Example:
1541  *<pre><code>
1542 var dt = new Date('1/10/2007');
1543 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1544 </code></pre>
1545  * @return {Number} The day number (0-6)
1546  */
1547 Date.prototype.getLastDayOfMonth = function() {
1548     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1549     return (day < 0) ? (day + 7) : day;
1550 };
1551
1552
1553 /**
1554  * Get the first date of this date's month
1555  * @return {Date}
1556  */
1557 Date.prototype.getFirstDateOfMonth = function() {
1558     return new Date(this.getFullYear(), this.getMonth(), 1);
1559 };
1560
1561 /**
1562  * Get the last date of this date's month
1563  * @return {Date}
1564  */
1565 Date.prototype.getLastDateOfMonth = function() {
1566     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1567 };
1568 /**
1569  * Get the number of days in the current month, adjusted for leap year.
1570  * @return {Number} The number of days in the month
1571  */
1572 Date.prototype.getDaysInMonth = function() {
1573     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1574     return Date.daysInMonth[this.getMonth()];
1575 };
1576
1577 /**
1578  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1579  * @return {String} 'st, 'nd', 'rd' or 'th'
1580  */
1581 Date.prototype.getSuffix = function() {
1582     switch (this.getDate()) {
1583         case 1:
1584         case 21:
1585         case 31:
1586             return "st";
1587         case 2:
1588         case 22:
1589             return "nd";
1590         case 3:
1591         case 23:
1592             return "rd";
1593         default:
1594             return "th";
1595     }
1596 };
1597
1598 // private
1599 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1600
1601 /**
1602  * An array of textual month names.
1603  * Override these values for international dates, for example...
1604  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1605  * @type Array
1606  * @static
1607  */
1608 Date.monthNames =
1609    ["January",
1610     "February",
1611     "March",
1612     "April",
1613     "May",
1614     "June",
1615     "July",
1616     "August",
1617     "September",
1618     "October",
1619     "November",
1620     "December"];
1621
1622 /**
1623  * An array of textual day names.
1624  * Override these values for international dates, for example...
1625  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1626  * @type Array
1627  * @static
1628  */
1629 Date.dayNames =
1630    ["Sunday",
1631     "Monday",
1632     "Tuesday",
1633     "Wednesday",
1634     "Thursday",
1635     "Friday",
1636     "Saturday"];
1637
1638 // private
1639 Date.y2kYear = 50;
1640 // private
1641 Date.monthNumbers = {
1642     Jan:0,
1643     Feb:1,
1644     Mar:2,
1645     Apr:3,
1646     May:4,
1647     Jun:5,
1648     Jul:6,
1649     Aug:7,
1650     Sep:8,
1651     Oct:9,
1652     Nov:10,
1653     Dec:11};
1654
1655 /**
1656  * Creates and returns a new Date instance with the exact same date value as the called instance.
1657  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1658  * variable will also be changed.  When the intention is to create a new variable that will not
1659  * modify the original instance, you should create a clone.
1660  *
1661  * Example of correctly cloning a date:
1662  * <pre><code>
1663 //wrong way:
1664 var orig = new Date('10/1/2006');
1665 var copy = orig;
1666 copy.setDate(5);
1667 document.write(orig);  //returns 'Thu Oct 05 2006'!
1668
1669 //correct way:
1670 var orig = new Date('10/1/2006');
1671 var copy = orig.clone();
1672 copy.setDate(5);
1673 document.write(orig);  //returns 'Thu Oct 01 2006'
1674 </code></pre>
1675  * @return {Date} The new Date instance
1676  */
1677 Date.prototype.clone = function() {
1678         return new Date(this.getTime());
1679 };
1680
1681 /**
1682  * Clears any time information from this date
1683  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1684  @return {Date} this or the clone
1685  */
1686 Date.prototype.clearTime = function(clone){
1687     if(clone){
1688         return this.clone().clearTime();
1689     }
1690     this.setHours(0);
1691     this.setMinutes(0);
1692     this.setSeconds(0);
1693     this.setMilliseconds(0);
1694     return this;
1695 };
1696
1697 // private
1698 // safari setMonth is broken -- check that this is only donw once...
1699 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1700     Date.brokenSetMonth = Date.prototype.setMonth;
1701         Date.prototype.setMonth = function(num){
1702                 if(num <= -1){
1703                         var n = Math.ceil(-num);
1704                         var back_year = Math.ceil(n/12);
1705                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1706                         this.setFullYear(this.getFullYear() - back_year);
1707                         return Date.brokenSetMonth.call(this, month);
1708                 } else {
1709                         return Date.brokenSetMonth.apply(this, arguments);
1710                 }
1711         };
1712 }
1713
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.MILLI = "ms";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.SECOND = "s";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MINUTE = "mi";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.HOUR = "h";
1730 /** Date interval constant 
1731 * @static 
1732 * @type String */
1733 Date.DAY = "d";
1734 /** Date interval constant 
1735 * @static 
1736 * @type String */
1737 Date.MONTH = "mo";
1738 /** Date interval constant 
1739 * @static 
1740 * @type String */
1741 Date.YEAR = "y";
1742
1743 /**
1744  * Provides a convenient method of performing basic date arithmetic.  This method
1745  * does not modify the Date instance being called - it creates and returns
1746  * a new Date instance containing the resulting date value.
1747  *
1748  * Examples:
1749  * <pre><code>
1750 //Basic usage:
1751 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1752 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1753
1754 //Negative values will subtract correctly:
1755 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1756 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1757
1758 //You can even chain several calls together in one line!
1759 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1760 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1761  </code></pre>
1762  *
1763  * @param {String} interval   A valid date interval enum value
1764  * @param {Number} value      The amount to add to the current date
1765  * @return {Date} The new Date instance
1766  */
1767 Date.prototype.add = function(interval, value){
1768   var d = this.clone();
1769   if (!interval || value === 0) { return d; }
1770   switch(interval.toLowerCase()){
1771     case Date.MILLI:
1772       d.setMilliseconds(this.getMilliseconds() + value);
1773       break;
1774     case Date.SECOND:
1775       d.setSeconds(this.getSeconds() + value);
1776       break;
1777     case Date.MINUTE:
1778       d.setMinutes(this.getMinutes() + value);
1779       break;
1780     case Date.HOUR:
1781       d.setHours(this.getHours() + value);
1782       break;
1783     case Date.DAY:
1784       d.setDate(this.getDate() + value);
1785       break;
1786     case Date.MONTH:
1787       var day = this.getDate();
1788       if(day > 28){
1789           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1790       }
1791       d.setDate(day);
1792       d.setMonth(this.getMonth() + value);
1793       break;
1794     case Date.YEAR:
1795       d.setFullYear(this.getFullYear() + value);
1796       break;
1797   }
1798   return d;
1799 };
1800 /*
1801  * Based on:
1802  * Ext JS Library 1.1.1
1803  * Copyright(c) 2006-2007, Ext JS, LLC.
1804  *
1805  * Originally Released Under LGPL - original licence link has changed is not relivant.
1806  *
1807  * Fork - LGPL
1808  * <script type="text/javascript">
1809  */
1810
1811 /**
1812  * @class Roo.lib.Dom
1813  * @static
1814  * 
1815  * Dom utils (from YIU afaik)
1816  * 
1817  **/
1818 Roo.lib.Dom = {
1819     /**
1820      * Get the view width
1821      * @param {Boolean} full True will get the full document, otherwise it's the view width
1822      * @return {Number} The width
1823      */
1824      
1825     getViewWidth : function(full) {
1826         return full ? this.getDocumentWidth() : this.getViewportWidth();
1827     },
1828     /**
1829      * Get the view height
1830      * @param {Boolean} full True will get the full document, otherwise it's the view height
1831      * @return {Number} The height
1832      */
1833     getViewHeight : function(full) {
1834         return full ? this.getDocumentHeight() : this.getViewportHeight();
1835     },
1836
1837     getDocumentHeight: function() {
1838         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1839         return Math.max(scrollHeight, this.getViewportHeight());
1840     },
1841
1842     getDocumentWidth: function() {
1843         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1844         return Math.max(scrollWidth, this.getViewportWidth());
1845     },
1846
1847     getViewportHeight: function() {
1848         var height = self.innerHeight;
1849         var mode = document.compatMode;
1850
1851         if ((mode || Roo.isIE) && !Roo.isOpera) {
1852             height = (mode == "CSS1Compat") ?
1853                      document.documentElement.clientHeight :
1854                      document.body.clientHeight;
1855         }
1856
1857         return height;
1858     },
1859
1860     getViewportWidth: function() {
1861         var width = self.innerWidth;
1862         var mode = document.compatMode;
1863
1864         if (mode || Roo.isIE) {
1865             width = (mode == "CSS1Compat") ?
1866                     document.documentElement.clientWidth :
1867                     document.body.clientWidth;
1868         }
1869         return width;
1870     },
1871
1872     isAncestor : function(p, c) {
1873         p = Roo.getDom(p);
1874         c = Roo.getDom(c);
1875         if (!p || !c) {
1876             return false;
1877         }
1878
1879         if (p.contains && !Roo.isSafari) {
1880             return p.contains(c);
1881         } else if (p.compareDocumentPosition) {
1882             return !!(p.compareDocumentPosition(c) & 16);
1883         } else {
1884             var parent = c.parentNode;
1885             while (parent) {
1886                 if (parent == p) {
1887                     return true;
1888                 }
1889                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1890                     return false;
1891                 }
1892                 parent = parent.parentNode;
1893             }
1894             return false;
1895         }
1896     },
1897
1898     getRegion : function(el) {
1899         return Roo.lib.Region.getRegion(el);
1900     },
1901
1902     getY : function(el) {
1903         return this.getXY(el)[1];
1904     },
1905
1906     getX : function(el) {
1907         return this.getXY(el)[0];
1908     },
1909
1910     getXY : function(el) {
1911         var p, pe, b, scroll, bd = document.body;
1912         el = Roo.getDom(el);
1913         var fly = Roo.lib.AnimBase.fly;
1914         if (el.getBoundingClientRect) {
1915             b = el.getBoundingClientRect();
1916             scroll = fly(document).getScroll();
1917             return [b.left + scroll.left, b.top + scroll.top];
1918         }
1919         var x = 0, y = 0;
1920
1921         p = el;
1922
1923         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1924
1925         while (p) {
1926
1927             x += p.offsetLeft;
1928             y += p.offsetTop;
1929
1930             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1931                 hasAbsolute = true;
1932             }
1933
1934             if (Roo.isGecko) {
1935                 pe = fly(p);
1936
1937                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1938                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1939
1940
1941                 x += bl;
1942                 y += bt;
1943
1944
1945                 if (p != el && pe.getStyle('overflow') != 'visible') {
1946                     x += bl;
1947                     y += bt;
1948                 }
1949             }
1950             p = p.offsetParent;
1951         }
1952
1953         if (Roo.isSafari && hasAbsolute) {
1954             x -= bd.offsetLeft;
1955             y -= bd.offsetTop;
1956         }
1957
1958         if (Roo.isGecko && !hasAbsolute) {
1959             var dbd = fly(bd);
1960             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1961             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1962         }
1963
1964         p = el.parentNode;
1965         while (p && p != bd) {
1966             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1967                 x -= p.scrollLeft;
1968                 y -= p.scrollTop;
1969             }
1970             p = p.parentNode;
1971         }
1972         return [x, y];
1973     },
1974  
1975   
1976
1977
1978     setXY : function(el, xy) {
1979         el = Roo.fly(el, '_setXY');
1980         el.position();
1981         var pts = el.translatePoints(xy);
1982         if (xy[0] !== false) {
1983             el.dom.style.left = pts.left + "px";
1984         }
1985         if (xy[1] !== false) {
1986             el.dom.style.top = pts.top + "px";
1987         }
1988     },
1989
1990     setX : function(el, x) {
1991         this.setXY(el, [x, false]);
1992     },
1993
1994     setY : function(el, y) {
1995         this.setXY(el, [false, y]);
1996     }
1997 };
1998 /*
1999  * Portions of this file are based on pieces of Yahoo User Interface Library
2000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2001  * YUI licensed under the BSD License:
2002  * http://developer.yahoo.net/yui/license.txt
2003  * <script type="text/javascript">
2004  *
2005  */
2006
2007 Roo.lib.Event = function() {
2008     var loadComplete = false;
2009     var listeners = [];
2010     var unloadListeners = [];
2011     var retryCount = 0;
2012     var onAvailStack = [];
2013     var counter = 0;
2014     var lastError = null;
2015
2016     return {
2017         POLL_RETRYS: 200,
2018         POLL_INTERVAL: 20,
2019         EL: 0,
2020         TYPE: 1,
2021         FN: 2,
2022         WFN: 3,
2023         OBJ: 3,
2024         ADJ_SCOPE: 4,
2025         _interval: null,
2026
2027         startInterval: function() {
2028             if (!this._interval) {
2029                 var self = this;
2030                 var callback = function() {
2031                     self._tryPreloadAttach();
2032                 };
2033                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2034
2035             }
2036         },
2037
2038         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2039             onAvailStack.push({ id:         p_id,
2040                 fn:         p_fn,
2041                 obj:        p_obj,
2042                 override:   p_override,
2043                 checkReady: false    });
2044
2045             retryCount = this.POLL_RETRYS;
2046             this.startInterval();
2047         },
2048
2049
2050         addListener: function(el, eventName, fn) {
2051             el = Roo.getDom(el);
2052             if (!el || !fn) {
2053                 return false;
2054             }
2055
2056             if ("unload" == eventName) {
2057                 unloadListeners[unloadListeners.length] =
2058                 [el, eventName, fn];
2059                 return true;
2060             }
2061
2062             var wrappedFn = function(e) {
2063                 return fn(Roo.lib.Event.getEvent(e));
2064             };
2065
2066             var li = [el, eventName, fn, wrappedFn];
2067
2068             var index = listeners.length;
2069             listeners[index] = li;
2070
2071             this.doAdd(el, eventName, wrappedFn, false);
2072             return true;
2073
2074         },
2075
2076
2077         removeListener: function(el, eventName, fn) {
2078             var i, len;
2079
2080             el = Roo.getDom(el);
2081
2082             if(!fn) {
2083                 return this.purgeElement(el, false, eventName);
2084             }
2085
2086
2087             if ("unload" == eventName) {
2088
2089                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2090                     var li = unloadListeners[i];
2091                     if (li &&
2092                         li[0] == el &&
2093                         li[1] == eventName &&
2094                         li[2] == fn) {
2095                         unloadListeners.splice(i, 1);
2096                         return true;
2097                     }
2098                 }
2099
2100                 return false;
2101             }
2102
2103             var cacheItem = null;
2104
2105
2106             var index = arguments[3];
2107
2108             if ("undefined" == typeof index) {
2109                 index = this._getCacheIndex(el, eventName, fn);
2110             }
2111
2112             if (index >= 0) {
2113                 cacheItem = listeners[index];
2114             }
2115
2116             if (!el || !cacheItem) {
2117                 return false;
2118             }
2119
2120             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2121
2122             delete listeners[index][this.WFN];
2123             delete listeners[index][this.FN];
2124             listeners.splice(index, 1);
2125
2126             return true;
2127
2128         },
2129
2130
2131         getTarget: function(ev, resolveTextNode) {
2132             ev = ev.browserEvent || ev;
2133             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2134             var t = ev.target || ev.srcElement;
2135             return this.resolveTextNode(t);
2136         },
2137
2138
2139         resolveTextNode: function(node) {
2140             if (Roo.isSafari && node && 3 == node.nodeType) {
2141                 return node.parentNode;
2142             } else {
2143                 return node;
2144             }
2145         },
2146
2147
2148         getPageX: function(ev) {
2149             ev = ev.browserEvent || ev;
2150             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2151             var x = ev.pageX;
2152             if (!x && 0 !== x) {
2153                 x = ev.clientX || 0;
2154
2155                 if (Roo.isIE) {
2156                     x += this.getScroll()[1];
2157                 }
2158             }
2159
2160             return x;
2161         },
2162
2163
2164         getPageY: function(ev) {
2165             ev = ev.browserEvent || ev;
2166             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2167             var y = ev.pageY;
2168             if (!y && 0 !== y) {
2169                 y = ev.clientY || 0;
2170
2171                 if (Roo.isIE) {
2172                     y += this.getScroll()[0];
2173                 }
2174             }
2175
2176
2177             return y;
2178         },
2179
2180
2181         getXY: function(ev) {
2182             ev = ev.browserEvent || ev;
2183             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2184             return [this.getPageX(ev), this.getPageY(ev)];
2185         },
2186
2187
2188         getRelatedTarget: function(ev) {
2189             ev = ev.browserEvent || ev;
2190             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2191             var t = ev.relatedTarget;
2192             if (!t) {
2193                 if (ev.type == "mouseout") {
2194                     t = ev.toElement;
2195                 } else if (ev.type == "mouseover") {
2196                     t = ev.fromElement;
2197                 }
2198             }
2199
2200             return this.resolveTextNode(t);
2201         },
2202
2203
2204         getTime: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2207             if (!ev.time) {
2208                 var t = new Date().getTime();
2209                 try {
2210                     ev.time = t;
2211                 } catch(ex) {
2212                     this.lastError = ex;
2213                     return t;
2214                 }
2215             }
2216
2217             return ev.time;
2218         },
2219
2220
2221         stopEvent: function(ev) {
2222             this.stopPropagation(ev);
2223             this.preventDefault(ev);
2224         },
2225
2226
2227         stopPropagation: function(ev) {
2228             ev = ev.browserEvent || ev;
2229             if (ev.stopPropagation) {
2230                 ev.stopPropagation();
2231             } else {
2232                 ev.cancelBubble = true;
2233             }
2234         },
2235
2236
2237         preventDefault: function(ev) {
2238             ev = ev.browserEvent || ev;
2239             if(ev.preventDefault) {
2240                 ev.preventDefault();
2241             } else {
2242                 ev.returnValue = false;
2243             }
2244         },
2245
2246
2247         getEvent: function(e) {
2248             var ev = e || window.event;
2249             if (!ev) {
2250                 var c = this.getEvent.caller;
2251                 while (c) {
2252                     ev = c.arguments[0];
2253                     if (ev && Event == ev.constructor) {
2254                         break;
2255                     }
2256                     c = c.caller;
2257                 }
2258             }
2259             return ev;
2260         },
2261
2262
2263         getCharCode: function(ev) {
2264             ev = ev.browserEvent || ev;
2265             return ev.charCode || ev.keyCode || 0;
2266         },
2267
2268
2269         _getCacheIndex: function(el, eventName, fn) {
2270             for (var i = 0,len = listeners.length; i < len; ++i) {
2271                 var li = listeners[i];
2272                 if (li &&
2273                     li[this.FN] == fn &&
2274                     li[this.EL] == el &&
2275                     li[this.TYPE] == eventName) {
2276                     return i;
2277                 }
2278             }
2279
2280             return -1;
2281         },
2282
2283
2284         elCache: {},
2285
2286
2287         getEl: function(id) {
2288             return document.getElementById(id);
2289         },
2290
2291
2292         clearCache: function() {
2293         },
2294
2295
2296         _load: function(e) {
2297             loadComplete = true;
2298             var EU = Roo.lib.Event;
2299
2300
2301             if (Roo.isIE) {
2302                 EU.doRemove(window, "load", EU._load);
2303             }
2304         },
2305
2306
2307         _tryPreloadAttach: function() {
2308
2309             if (this.locked) {
2310                 return false;
2311             }
2312
2313             this.locked = true;
2314
2315
2316             var tryAgain = !loadComplete;
2317             if (!tryAgain) {
2318                 tryAgain = (retryCount > 0);
2319             }
2320
2321
2322             var notAvail = [];
2323             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2324                 var item = onAvailStack[i];
2325                 if (item) {
2326                     var el = this.getEl(item.id);
2327
2328                     if (el) {
2329                         if (!item.checkReady ||
2330                             loadComplete ||
2331                             el.nextSibling ||
2332                             (document && document.body)) {
2333
2334                             var scope = el;
2335                             if (item.override) {
2336                                 if (item.override === true) {
2337                                     scope = item.obj;
2338                                 } else {
2339                                     scope = item.override;
2340                                 }
2341                             }
2342                             item.fn.call(scope, item.obj);
2343                             onAvailStack[i] = null;
2344                         }
2345                     } else {
2346                         notAvail.push(item);
2347                     }
2348                 }
2349             }
2350
2351             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2352
2353             if (tryAgain) {
2354
2355                 this.startInterval();
2356             } else {
2357                 clearInterval(this._interval);
2358                 this._interval = null;
2359             }
2360
2361             this.locked = false;
2362
2363             return true;
2364
2365         },
2366
2367
2368         purgeElement: function(el, recurse, eventName) {
2369             var elListeners = this.getListeners(el, eventName);
2370             if (elListeners) {
2371                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2372                     var l = elListeners[i];
2373                     this.removeListener(el, l.type, l.fn);
2374                 }
2375             }
2376
2377             if (recurse && el && el.childNodes) {
2378                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2379                     this.purgeElement(el.childNodes[i], recurse, eventName);
2380                 }
2381             }
2382         },
2383
2384
2385         getListeners: function(el, eventName) {
2386             var results = [], searchLists;
2387             if (!eventName) {
2388                 searchLists = [listeners, unloadListeners];
2389             } else if (eventName == "unload") {
2390                 searchLists = [unloadListeners];
2391             } else {
2392                 searchLists = [listeners];
2393             }
2394
2395             for (var j = 0; j < searchLists.length; ++j) {
2396                 var searchList = searchLists[j];
2397                 if (searchList && searchList.length > 0) {
2398                     for (var i = 0,len = searchList.length; i < len; ++i) {
2399                         var l = searchList[i];
2400                         if (l && l[this.EL] === el &&
2401                             (!eventName || eventName === l[this.TYPE])) {
2402                             results.push({
2403                                 type:   l[this.TYPE],
2404                                 fn:     l[this.FN],
2405                                 obj:    l[this.OBJ],
2406                                 adjust: l[this.ADJ_SCOPE],
2407                                 index:  i
2408                             });
2409                         }
2410                     }
2411                 }
2412             }
2413
2414             return (results.length) ? results : null;
2415         },
2416
2417
2418         _unload: function(e) {
2419
2420             var EU = Roo.lib.Event, i, j, l, len, index;
2421
2422             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2423                 l = unloadListeners[i];
2424                 if (l) {
2425                     var scope = window;
2426                     if (l[EU.ADJ_SCOPE]) {
2427                         if (l[EU.ADJ_SCOPE] === true) {
2428                             scope = l[EU.OBJ];
2429                         } else {
2430                             scope = l[EU.ADJ_SCOPE];
2431                         }
2432                     }
2433                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2434                     unloadListeners[i] = null;
2435                     l = null;
2436                     scope = null;
2437                 }
2438             }
2439
2440             unloadListeners = null;
2441
2442             if (listeners && listeners.length > 0) {
2443                 j = listeners.length;
2444                 while (j) {
2445                     index = j - 1;
2446                     l = listeners[index];
2447                     if (l) {
2448                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2449                                 l[EU.FN], index);
2450                     }
2451                     j = j - 1;
2452                 }
2453                 l = null;
2454
2455                 EU.clearCache();
2456             }
2457
2458             EU.doRemove(window, "unload", EU._unload);
2459
2460         },
2461
2462
2463         getScroll: function() {
2464             var dd = document.documentElement, db = document.body;
2465             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2466                 return [dd.scrollTop, dd.scrollLeft];
2467             } else if (db) {
2468                 return [db.scrollTop, db.scrollLeft];
2469             } else {
2470                 return [0, 0];
2471             }
2472         },
2473
2474
2475         doAdd: function () {
2476             if (window.addEventListener) {
2477                 return function(el, eventName, fn, capture) {
2478                     el.addEventListener(eventName, fn, (capture));
2479                 };
2480             } else if (window.attachEvent) {
2481                 return function(el, eventName, fn, capture) {
2482                     el.attachEvent("on" + eventName, fn);
2483                 };
2484             } else {
2485                 return function() {
2486                 };
2487             }
2488         }(),
2489
2490
2491         doRemove: function() {
2492             if (window.removeEventListener) {
2493                 return function (el, eventName, fn, capture) {
2494                     el.removeEventListener(eventName, fn, (capture));
2495                 };
2496             } else if (window.detachEvent) {
2497                 return function (el, eventName, fn) {
2498                     el.detachEvent("on" + eventName, fn);
2499                 };
2500             } else {
2501                 return function() {
2502                 };
2503             }
2504         }()
2505     };
2506     
2507 }();
2508 (function() {     
2509    
2510     var E = Roo.lib.Event;
2511     E.on = E.addListener;
2512     E.un = E.removeListener;
2513
2514     if (document && document.body) {
2515         E._load();
2516     } else {
2517         E.doAdd(window, "load", E._load);
2518     }
2519     E.doAdd(window, "unload", E._unload);
2520     E._tryPreloadAttach();
2521 })();
2522
2523 /*
2524  * Portions of this file are based on pieces of Yahoo User Interface Library
2525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2526  * YUI licensed under the BSD License:
2527  * http://developer.yahoo.net/yui/license.txt
2528  * <script type="text/javascript">
2529  *
2530  */
2531
2532 (function() {
2533     /**
2534      * @class Roo.lib.Ajax
2535      *
2536      */
2537     Roo.lib.Ajax = {
2538         /**
2539          * @static 
2540          */
2541         request : function(method, uri, cb, data, options) {
2542             if(options){
2543                 var hs = options.headers;
2544                 if(hs){
2545                     for(var h in hs){
2546                         if(hs.hasOwnProperty(h)){
2547                             this.initHeader(h, hs[h], false);
2548                         }
2549                     }
2550                 }
2551                 if(options.xmlData){
2552                     this.initHeader('Content-Type', 'text/xml', false);
2553                     method = 'POST';
2554                     data = options.xmlData;
2555                 }
2556             }
2557
2558             return this.asyncRequest(method, uri, cb, data);
2559         },
2560
2561         serializeForm : function(form) {
2562             if(typeof form == 'string') {
2563                 form = (document.getElementById(form) || document.forms[form]);
2564             }
2565
2566             var el, name, val, disabled, data = '', hasSubmit = false;
2567             for (var i = 0; i < form.elements.length; i++) {
2568                 el = form.elements[i];
2569                 disabled = form.elements[i].disabled;
2570                 name = form.elements[i].name;
2571                 val = form.elements[i].value;
2572
2573                 if (!disabled && name){
2574                     switch (el.type)
2575                             {
2576                         case 'select-one':
2577                         case 'select-multiple':
2578                             for (var j = 0; j < el.options.length; j++) {
2579                                 if (el.options[j].selected) {
2580                                     if (Roo.isIE) {
2581                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2582                                     }
2583                                     else {
2584                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2585                                     }
2586                                 }
2587                             }
2588                             break;
2589                         case 'radio':
2590                         case 'checkbox':
2591                             if (el.checked) {
2592                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2593                             }
2594                             break;
2595                         case 'file':
2596
2597                         case undefined:
2598
2599                         case 'reset':
2600
2601                         case 'button':
2602
2603                             break;
2604                         case 'submit':
2605                             if(hasSubmit == false) {
2606                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2607                                 hasSubmit = true;
2608                             }
2609                             break;
2610                         default:
2611                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2612                             break;
2613                     }
2614                 }
2615             }
2616             data = data.substr(0, data.length - 1);
2617             return data;
2618         },
2619
2620         headers:{},
2621
2622         hasHeaders:false,
2623
2624         useDefaultHeader:true,
2625
2626         defaultPostHeader:'application/x-www-form-urlencoded',
2627
2628         useDefaultXhrHeader:true,
2629
2630         defaultXhrHeader:'XMLHttpRequest',
2631
2632         hasDefaultHeaders:true,
2633
2634         defaultHeaders:{},
2635
2636         poll:{},
2637
2638         timeout:{},
2639
2640         pollInterval:50,
2641
2642         transactionId:0,
2643
2644         setProgId:function(id)
2645         {
2646             this.activeX.unshift(id);
2647         },
2648
2649         setDefaultPostHeader:function(b)
2650         {
2651             this.useDefaultHeader = b;
2652         },
2653
2654         setDefaultXhrHeader:function(b)
2655         {
2656             this.useDefaultXhrHeader = b;
2657         },
2658
2659         setPollingInterval:function(i)
2660         {
2661             if (typeof i == 'number' && isFinite(i)) {
2662                 this.pollInterval = i;
2663             }
2664         },
2665
2666         createXhrObject:function(transactionId)
2667         {
2668             var obj,http;
2669             try
2670             {
2671
2672                 http = new XMLHttpRequest();
2673
2674                 obj = { conn:http, tId:transactionId };
2675             }
2676             catch(e)
2677             {
2678                 for (var i = 0; i < this.activeX.length; ++i) {
2679                     try
2680                     {
2681
2682                         http = new ActiveXObject(this.activeX[i]);
2683
2684                         obj = { conn:http, tId:transactionId };
2685                         break;
2686                     }
2687                     catch(e) {
2688                     }
2689                 }
2690             }
2691             finally
2692             {
2693                 return obj;
2694             }
2695         },
2696
2697         getConnectionObject:function()
2698         {
2699             var o;
2700             var tId = this.transactionId;
2701
2702             try
2703             {
2704                 o = this.createXhrObject(tId);
2705                 if (o) {
2706                     this.transactionId++;
2707                 }
2708             }
2709             catch(e) {
2710             }
2711             finally
2712             {
2713                 return o;
2714             }
2715         },
2716
2717         asyncRequest:function(method, uri, callback, postData)
2718         {
2719             var o = this.getConnectionObject();
2720
2721             if (!o) {
2722                 return null;
2723             }
2724             else {
2725                 o.conn.open(method, uri, true);
2726
2727                 if (this.useDefaultXhrHeader) {
2728                     if (!this.defaultHeaders['X-Requested-With']) {
2729                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2730                     }
2731                 }
2732
2733                 if(postData && this.useDefaultHeader){
2734                     this.initHeader('Content-Type', this.defaultPostHeader);
2735                 }
2736
2737                  if (this.hasDefaultHeaders || this.hasHeaders) {
2738                     this.setHeader(o);
2739                 }
2740
2741                 this.handleReadyState(o, callback);
2742                 o.conn.send(postData || null);
2743
2744                 return o;
2745             }
2746         },
2747
2748         handleReadyState:function(o, callback)
2749         {
2750             var oConn = this;
2751
2752             if (callback && callback.timeout) {
2753                 
2754                 this.timeout[o.tId] = window.setTimeout(function() {
2755                     oConn.abort(o, callback, true);
2756                 }, callback.timeout);
2757             }
2758
2759             this.poll[o.tId] = window.setInterval(
2760                     function() {
2761                         if (o.conn && o.conn.readyState == 4) {
2762                             window.clearInterval(oConn.poll[o.tId]);
2763                             delete oConn.poll[o.tId];
2764
2765                             if(callback && callback.timeout) {
2766                                 window.clearTimeout(oConn.timeout[o.tId]);
2767                                 delete oConn.timeout[o.tId];
2768                             }
2769
2770                             oConn.handleTransactionResponse(o, callback);
2771                         }
2772                     }
2773                     , this.pollInterval);
2774         },
2775
2776         handleTransactionResponse:function(o, callback, isAbort)
2777         {
2778
2779             if (!callback) {
2780                 this.releaseObject(o);
2781                 return;
2782             }
2783
2784             var httpStatus, responseObject;
2785
2786             try
2787             {
2788                 if (o.conn.status !== undefined && o.conn.status != 0) {
2789                     httpStatus = o.conn.status;
2790                 }
2791                 else {
2792                     httpStatus = 13030;
2793                 }
2794             }
2795             catch(e) {
2796
2797
2798                 httpStatus = 13030;
2799             }
2800
2801             if (httpStatus >= 200 && httpStatus < 300) {
2802                 responseObject = this.createResponseObject(o, callback.argument);
2803                 if (callback.success) {
2804                     if (!callback.scope) {
2805                         callback.success(responseObject);
2806                     }
2807                     else {
2808
2809
2810                         callback.success.apply(callback.scope, [responseObject]);
2811                     }
2812                 }
2813             }
2814             else {
2815                 switch (httpStatus) {
2816
2817                     case 12002:
2818                     case 12029:
2819                     case 12030:
2820                     case 12031:
2821                     case 12152:
2822                     case 13030:
2823                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2824                         if (callback.failure) {
2825                             if (!callback.scope) {
2826                                 callback.failure(responseObject);
2827                             }
2828                             else {
2829                                 callback.failure.apply(callback.scope, [responseObject]);
2830                             }
2831                         }
2832                         break;
2833                     default:
2834                         responseObject = this.createResponseObject(o, callback.argument);
2835                         if (callback.failure) {
2836                             if (!callback.scope) {
2837                                 callback.failure(responseObject);
2838                             }
2839                             else {
2840                                 callback.failure.apply(callback.scope, [responseObject]);
2841                             }
2842                         }
2843                 }
2844             }
2845
2846             this.releaseObject(o);
2847             responseObject = null;
2848         },
2849
2850         createResponseObject:function(o, callbackArg)
2851         {
2852             var obj = {};
2853             var headerObj = {};
2854
2855             try
2856             {
2857                 var headerStr = o.conn.getAllResponseHeaders();
2858                 var header = headerStr.split('\n');
2859                 for (var i = 0; i < header.length; i++) {
2860                     var delimitPos = header[i].indexOf(':');
2861                     if (delimitPos != -1) {
2862                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2863                     }
2864                 }
2865             }
2866             catch(e) {
2867             }
2868
2869             obj.tId = o.tId;
2870             obj.status = o.conn.status;
2871             obj.statusText = o.conn.statusText;
2872             obj.getResponseHeader = headerObj;
2873             obj.getAllResponseHeaders = headerStr;
2874             obj.responseText = o.conn.responseText;
2875             obj.responseXML = o.conn.responseXML;
2876
2877             if (typeof callbackArg !== undefined) {
2878                 obj.argument = callbackArg;
2879             }
2880
2881             return obj;
2882         },
2883
2884         createExceptionObject:function(tId, callbackArg, isAbort)
2885         {
2886             var COMM_CODE = 0;
2887             var COMM_ERROR = 'communication failure';
2888             var ABORT_CODE = -1;
2889             var ABORT_ERROR = 'transaction aborted';
2890
2891             var obj = {};
2892
2893             obj.tId = tId;
2894             if (isAbort) {
2895                 obj.status = ABORT_CODE;
2896                 obj.statusText = ABORT_ERROR;
2897             }
2898             else {
2899                 obj.status = COMM_CODE;
2900                 obj.statusText = COMM_ERROR;
2901             }
2902
2903             if (callbackArg) {
2904                 obj.argument = callbackArg;
2905             }
2906
2907             return obj;
2908         },
2909
2910         initHeader:function(label, value, isDefault)
2911         {
2912             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2913
2914             if (headerObj[label] === undefined) {
2915                 headerObj[label] = value;
2916             }
2917             else {
2918
2919
2920                 headerObj[label] = value + "," + headerObj[label];
2921             }
2922
2923             if (isDefault) {
2924                 this.hasDefaultHeaders = true;
2925             }
2926             else {
2927                 this.hasHeaders = true;
2928             }
2929         },
2930
2931
2932         setHeader:function(o)
2933         {
2934             if (this.hasDefaultHeaders) {
2935                 for (var prop in this.defaultHeaders) {
2936                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2937                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2938                     }
2939                 }
2940             }
2941
2942             if (this.hasHeaders) {
2943                 for (var prop in this.headers) {
2944                     if (this.headers.hasOwnProperty(prop)) {
2945                         o.conn.setRequestHeader(prop, this.headers[prop]);
2946                     }
2947                 }
2948                 this.headers = {};
2949                 this.hasHeaders = false;
2950             }
2951         },
2952
2953         resetDefaultHeaders:function() {
2954             delete this.defaultHeaders;
2955             this.defaultHeaders = {};
2956             this.hasDefaultHeaders = false;
2957         },
2958
2959         abort:function(o, callback, isTimeout)
2960         {
2961             if(this.isCallInProgress(o)) {
2962                 o.conn.abort();
2963                 window.clearInterval(this.poll[o.tId]);
2964                 delete this.poll[o.tId];
2965                 if (isTimeout) {
2966                     delete this.timeout[o.tId];
2967                 }
2968
2969                 this.handleTransactionResponse(o, callback, true);
2970
2971                 return true;
2972             }
2973             else {
2974                 return false;
2975             }
2976         },
2977
2978
2979         isCallInProgress:function(o)
2980         {
2981             if (o && o.conn) {
2982                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2983             }
2984             else {
2985
2986                 return false;
2987             }
2988         },
2989
2990
2991         releaseObject:function(o)
2992         {
2993
2994             o.conn = null;
2995
2996             o = null;
2997         },
2998
2999         activeX:[
3000         'MSXML2.XMLHTTP.3.0',
3001         'MSXML2.XMLHTTP',
3002         'Microsoft.XMLHTTP'
3003         ]
3004
3005
3006     };
3007 })();/*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015
3016 Roo.lib.Region = function(t, r, b, l) {
3017     this.top = t;
3018     this[1] = t;
3019     this.right = r;
3020     this.bottom = b;
3021     this.left = l;
3022     this[0] = l;
3023 };
3024
3025
3026 Roo.lib.Region.prototype = {
3027     contains : function(region) {
3028         return ( region.left >= this.left &&
3029                  region.right <= this.right &&
3030                  region.top >= this.top &&
3031                  region.bottom <= this.bottom    );
3032
3033     },
3034
3035     getArea : function() {
3036         return ( (this.bottom - this.top) * (this.right - this.left) );
3037     },
3038
3039     intersect : function(region) {
3040         var t = Math.max(this.top, region.top);
3041         var r = Math.min(this.right, region.right);
3042         var b = Math.min(this.bottom, region.bottom);
3043         var l = Math.max(this.left, region.left);
3044
3045         if (b >= t && r >= l) {
3046             return new Roo.lib.Region(t, r, b, l);
3047         } else {
3048             return null;
3049         }
3050     },
3051     union : function(region) {
3052         var t = Math.min(this.top, region.top);
3053         var r = Math.max(this.right, region.right);
3054         var b = Math.max(this.bottom, region.bottom);
3055         var l = Math.min(this.left, region.left);
3056
3057         return new Roo.lib.Region(t, r, b, l);
3058     },
3059
3060     adjust : function(t, l, b, r) {
3061         this.top += t;
3062         this.left += l;
3063         this.right += r;
3064         this.bottom += b;
3065         return this;
3066     }
3067 };
3068
3069 Roo.lib.Region.getRegion = function(el) {
3070     var p = Roo.lib.Dom.getXY(el);
3071
3072     var t = p[1];
3073     var r = p[0] + el.offsetWidth;
3074     var b = p[1] + el.offsetHeight;
3075     var l = p[0];
3076
3077     return new Roo.lib.Region(t, r, b, l);
3078 };
3079 /*
3080  * Portions of this file are based on pieces of Yahoo User Interface Library
3081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3082  * YUI licensed under the BSD License:
3083  * http://developer.yahoo.net/yui/license.txt
3084  * <script type="text/javascript">
3085  *
3086  */
3087 //@@dep Roo.lib.Region
3088
3089
3090 Roo.lib.Point = function(x, y) {
3091     if (x instanceof Array) {
3092         y = x[1];
3093         x = x[0];
3094     }
3095     this.x = this.right = this.left = this[0] = x;
3096     this.y = this.top = this.bottom = this[1] = y;
3097 };
3098
3099 Roo.lib.Point.prototype = new Roo.lib.Region();
3100 /*
3101  * Portions of this file are based on pieces of Yahoo User Interface Library
3102  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3103  * YUI licensed under the BSD License:
3104  * http://developer.yahoo.net/yui/license.txt
3105  * <script type="text/javascript">
3106  *
3107  */
3108  
3109 (function() {   
3110
3111     Roo.lib.Anim = {
3112         scroll : function(el, args, duration, easing, cb, scope) {
3113             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3114         },
3115
3116         motion : function(el, args, duration, easing, cb, scope) {
3117             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3118         },
3119
3120         color : function(el, args, duration, easing, cb, scope) {
3121             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3122         },
3123
3124         run : function(el, args, duration, easing, cb, scope, type) {
3125             type = type || Roo.lib.AnimBase;
3126             if (typeof easing == "string") {
3127                 easing = Roo.lib.Easing[easing];
3128             }
3129             var anim = new type(el, args, duration, easing);
3130             anim.animateX(function() {
3131                 Roo.callback(cb, scope);
3132             });
3133             return anim;
3134         }
3135     };
3136 })();/*
3137  * Portions of this file are based on pieces of Yahoo User Interface Library
3138  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3139  * YUI licensed under the BSD License:
3140  * http://developer.yahoo.net/yui/license.txt
3141  * <script type="text/javascript">
3142  *
3143  */
3144
3145 (function() {    
3146     var libFlyweight;
3147     
3148     function fly(el) {
3149         if (!libFlyweight) {
3150             libFlyweight = new Roo.Element.Flyweight();
3151         }
3152         libFlyweight.dom = el;
3153         return libFlyweight;
3154     }
3155
3156     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3157     
3158    
3159     
3160     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3161         if (el) {
3162             this.init(el, attributes, duration, method);
3163         }
3164     };
3165
3166     Roo.lib.AnimBase.fly = fly;
3167     
3168     
3169     
3170     Roo.lib.AnimBase.prototype = {
3171
3172         toString: function() {
3173             var el = this.getEl();
3174             var id = el.id || el.tagName;
3175             return ("Anim " + id);
3176         },
3177
3178         patterns: {
3179             noNegatives:        /width|height|opacity|padding/i,
3180             offsetAttribute:  /^((width|height)|(top|left))$/,
3181             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3182             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3183         },
3184
3185
3186         doMethod: function(attr, start, end) {
3187             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3188         },
3189
3190
3191         setAttribute: function(attr, val, unit) {
3192             if (this.patterns.noNegatives.test(attr)) {
3193                 val = (val > 0) ? val : 0;
3194             }
3195
3196             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3197         },
3198
3199
3200         getAttribute: function(attr) {
3201             var el = this.getEl();
3202             var val = fly(el).getStyle(attr);
3203
3204             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3205                 return parseFloat(val);
3206             }
3207
3208             var a = this.patterns.offsetAttribute.exec(attr) || [];
3209             var pos = !!( a[3] );
3210             var box = !!( a[2] );
3211
3212
3213             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3214                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3215             } else {
3216                 val = 0;
3217             }
3218
3219             return val;
3220         },
3221
3222
3223         getDefaultUnit: function(attr) {
3224             if (this.patterns.defaultUnit.test(attr)) {
3225                 return 'px';
3226             }
3227
3228             return '';
3229         },
3230
3231         animateX : function(callback, scope) {
3232             var f = function() {
3233                 this.onComplete.removeListener(f);
3234                 if (typeof callback == "function") {
3235                     callback.call(scope || this, this);
3236                 }
3237             };
3238             this.onComplete.addListener(f, this);
3239             this.animate();
3240         },
3241
3242
3243         setRuntimeAttribute: function(attr) {
3244             var start;
3245             var end;
3246             var attributes = this.attributes;
3247
3248             this.runtimeAttributes[attr] = {};
3249
3250             var isset = function(prop) {
3251                 return (typeof prop !== 'undefined');
3252             };
3253
3254             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3255                 return false;
3256             }
3257
3258             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3259
3260
3261             if (isset(attributes[attr]['to'])) {
3262                 end = attributes[attr]['to'];
3263             } else if (isset(attributes[attr]['by'])) {
3264                 if (start.constructor == Array) {
3265                     end = [];
3266                     for (var i = 0, len = start.length; i < len; ++i) {
3267                         end[i] = start[i] + attributes[attr]['by'][i];
3268                     }
3269                 } else {
3270                     end = start + attributes[attr]['by'];
3271                 }
3272             }
3273
3274             this.runtimeAttributes[attr].start = start;
3275             this.runtimeAttributes[attr].end = end;
3276
3277
3278             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3279         },
3280
3281
3282         init: function(el, attributes, duration, method) {
3283
3284             var isAnimated = false;
3285
3286
3287             var startTime = null;
3288
3289
3290             var actualFrames = 0;
3291
3292
3293             el = Roo.getDom(el);
3294
3295
3296             this.attributes = attributes || {};
3297
3298
3299             this.duration = duration || 1;
3300
3301
3302             this.method = method || Roo.lib.Easing.easeNone;
3303
3304
3305             this.useSeconds = true;
3306
3307
3308             this.currentFrame = 0;
3309
3310
3311             this.totalFrames = Roo.lib.AnimMgr.fps;
3312
3313
3314             this.getEl = function() {
3315                 return el;
3316             };
3317
3318
3319             this.isAnimated = function() {
3320                 return isAnimated;
3321             };
3322
3323
3324             this.getStartTime = function() {
3325                 return startTime;
3326             };
3327
3328             this.runtimeAttributes = {};
3329
3330
3331             this.animate = function() {
3332                 if (this.isAnimated()) {
3333                     return false;
3334                 }
3335
3336                 this.currentFrame = 0;
3337
3338                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3339
3340                 Roo.lib.AnimMgr.registerElement(this);
3341             };
3342
3343
3344             this.stop = function(finish) {
3345                 if (finish) {
3346                     this.currentFrame = this.totalFrames;
3347                     this._onTween.fire();
3348                 }
3349                 Roo.lib.AnimMgr.stop(this);
3350             };
3351
3352             var onStart = function() {
3353                 this.onStart.fire();
3354
3355                 this.runtimeAttributes = {};
3356                 for (var attr in this.attributes) {
3357                     this.setRuntimeAttribute(attr);
3358                 }
3359
3360                 isAnimated = true;
3361                 actualFrames = 0;
3362                 startTime = new Date();
3363             };
3364
3365
3366             var onTween = function() {
3367                 var data = {
3368                     duration: new Date() - this.getStartTime(),
3369                     currentFrame: this.currentFrame
3370                 };
3371
3372                 data.toString = function() {
3373                     return (
3374                             'duration: ' + data.duration +
3375                             ', currentFrame: ' + data.currentFrame
3376                             );
3377                 };
3378
3379                 this.onTween.fire(data);
3380
3381                 var runtimeAttributes = this.runtimeAttributes;
3382
3383                 for (var attr in runtimeAttributes) {
3384                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3385                 }
3386
3387                 actualFrames += 1;
3388             };
3389
3390             var onComplete = function() {
3391                 var actual_duration = (new Date() - startTime) / 1000 ;
3392
3393                 var data = {
3394                     duration: actual_duration,
3395                     frames: actualFrames,
3396                     fps: actualFrames / actual_duration
3397                 };
3398
3399                 data.toString = function() {
3400                     return (
3401                             'duration: ' + data.duration +
3402                             ', frames: ' + data.frames +
3403                             ', fps: ' + data.fps
3404                             );
3405                 };
3406
3407                 isAnimated = false;
3408                 actualFrames = 0;
3409                 this.onComplete.fire(data);
3410             };
3411
3412
3413             this._onStart = new Roo.util.Event(this);
3414             this.onStart = new Roo.util.Event(this);
3415             this.onTween = new Roo.util.Event(this);
3416             this._onTween = new Roo.util.Event(this);
3417             this.onComplete = new Roo.util.Event(this);
3418             this._onComplete = new Roo.util.Event(this);
3419             this._onStart.addListener(onStart);
3420             this._onTween.addListener(onTween);
3421             this._onComplete.addListener(onComplete);
3422         }
3423     };
3424 })();
3425 /*
3426  * Portions of this file are based on pieces of Yahoo User Interface Library
3427  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3428  * YUI licensed under the BSD License:
3429  * http://developer.yahoo.net/yui/license.txt
3430  * <script type="text/javascript">
3431  *
3432  */
3433
3434 Roo.lib.AnimMgr = new function() {
3435
3436     var thread = null;
3437
3438
3439     var queue = [];
3440
3441
3442     var tweenCount = 0;
3443
3444
3445     this.fps = 1000;
3446
3447
3448     this.delay = 1;
3449
3450
3451     this.registerElement = function(tween) {
3452         queue[queue.length] = tween;
3453         tweenCount += 1;
3454         tween._onStart.fire();
3455         this.start();
3456     };
3457
3458
3459     this.unRegister = function(tween, index) {
3460         tween._onComplete.fire();
3461         index = index || getIndex(tween);
3462         if (index != -1) {
3463             queue.splice(index, 1);
3464         }
3465
3466         tweenCount -= 1;
3467         if (tweenCount <= 0) {
3468             this.stop();
3469         }
3470     };
3471
3472
3473     this.start = function() {
3474         if (thread === null) {
3475             thread = setInterval(this.run, this.delay);
3476         }
3477     };
3478
3479
3480     this.stop = function(tween) {
3481         if (!tween) {
3482             clearInterval(thread);
3483
3484             for (var i = 0, len = queue.length; i < len; ++i) {
3485                 if (queue[0].isAnimated()) {
3486                     this.unRegister(queue[0], 0);
3487                 }
3488             }
3489
3490             queue = [];
3491             thread = null;
3492             tweenCount = 0;
3493         }
3494         else {
3495             this.unRegister(tween);
3496         }
3497     };
3498
3499
3500     this.run = function() {
3501         for (var i = 0, len = queue.length; i < len; ++i) {
3502             var tween = queue[i];
3503             if (!tween || !tween.isAnimated()) {
3504                 continue;
3505             }
3506
3507             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3508             {
3509                 tween.currentFrame += 1;
3510
3511                 if (tween.useSeconds) {
3512                     correctFrame(tween);
3513                 }
3514                 tween._onTween.fire();
3515             }
3516             else {
3517                 Roo.lib.AnimMgr.stop(tween, i);
3518             }
3519         }
3520     };
3521
3522     var getIndex = function(anim) {
3523         for (var i = 0, len = queue.length; i < len; ++i) {
3524             if (queue[i] == anim) {
3525                 return i;
3526             }
3527         }
3528         return -1;
3529     };
3530
3531
3532     var correctFrame = function(tween) {
3533         var frames = tween.totalFrames;
3534         var frame = tween.currentFrame;
3535         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3536         var elapsed = (new Date() - tween.getStartTime());
3537         var tweak = 0;
3538
3539         if (elapsed < tween.duration * 1000) {
3540             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3541         } else {
3542             tweak = frames - (frame + 1);
3543         }
3544         if (tweak > 0 && isFinite(tweak)) {
3545             if (tween.currentFrame + tweak >= frames) {
3546                 tweak = frames - (frame + 1);
3547             }
3548
3549             tween.currentFrame += tweak;
3550         }
3551     };
3552 };
3553
3554     /*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 Roo.lib.Bezier = new function() {
3563
3564         this.getPosition = function(points, t) {
3565             var n = points.length;
3566             var tmp = [];
3567
3568             for (var i = 0; i < n; ++i) {
3569                 tmp[i] = [points[i][0], points[i][1]];
3570             }
3571
3572             for (var j = 1; j < n; ++j) {
3573                 for (i = 0; i < n - j; ++i) {
3574                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3575                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3576                 }
3577             }
3578
3579             return [ tmp[0][0], tmp[0][1] ];
3580
3581         };
3582     };/*
3583  * Portions of this file are based on pieces of Yahoo User Interface Library
3584  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3585  * YUI licensed under the BSD License:
3586  * http://developer.yahoo.net/yui/license.txt
3587  * <script type="text/javascript">
3588  *
3589  */
3590 (function() {
3591
3592     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3593         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3594     };
3595
3596     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3597
3598     var fly = Roo.lib.AnimBase.fly;
3599     var Y = Roo.lib;
3600     var superclass = Y.ColorAnim.superclass;
3601     var proto = Y.ColorAnim.prototype;
3602
3603     proto.toString = function() {
3604         var el = this.getEl();
3605         var id = el.id || el.tagName;
3606         return ("ColorAnim " + id);
3607     };
3608
3609     proto.patterns.color = /color$/i;
3610     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3611     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3612     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3613     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3614
3615
3616     proto.parseColor = function(s) {
3617         if (s.length == 3) {
3618             return s;
3619         }
3620
3621         var c = this.patterns.hex.exec(s);
3622         if (c && c.length == 4) {
3623             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3624         }
3625
3626         c = this.patterns.rgb.exec(s);
3627         if (c && c.length == 4) {
3628             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3629         }
3630
3631         c = this.patterns.hex3.exec(s);
3632         if (c && c.length == 4) {
3633             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3634         }
3635
3636         return null;
3637     };
3638     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3639     proto.getAttribute = function(attr) {
3640         var el = this.getEl();
3641         if (this.patterns.color.test(attr)) {
3642             var val = fly(el).getStyle(attr);
3643
3644             if (this.patterns.transparent.test(val)) {
3645                 var parent = el.parentNode;
3646                 val = fly(parent).getStyle(attr);
3647
3648                 while (parent && this.patterns.transparent.test(val)) {
3649                     parent = parent.parentNode;
3650                     val = fly(parent).getStyle(attr);
3651                     if (parent.tagName.toUpperCase() == 'HTML') {
3652                         val = '#fff';
3653                     }
3654                 }
3655             }
3656         } else {
3657             val = superclass.getAttribute.call(this, attr);
3658         }
3659
3660         return val;
3661     };
3662     proto.getAttribute = function(attr) {
3663         var el = this.getEl();
3664         if (this.patterns.color.test(attr)) {
3665             var val = fly(el).getStyle(attr);
3666
3667             if (this.patterns.transparent.test(val)) {
3668                 var parent = el.parentNode;
3669                 val = fly(parent).getStyle(attr);
3670
3671                 while (parent && this.patterns.transparent.test(val)) {
3672                     parent = parent.parentNode;
3673                     val = fly(parent).getStyle(attr);
3674                     if (parent.tagName.toUpperCase() == 'HTML') {
3675                         val = '#fff';
3676                     }
3677                 }
3678             }
3679         } else {
3680             val = superclass.getAttribute.call(this, attr);
3681         }
3682
3683         return val;
3684     };
3685
3686     proto.doMethod = function(attr, start, end) {
3687         var val;
3688
3689         if (this.patterns.color.test(attr)) {
3690             val = [];
3691             for (var i = 0, len = start.length; i < len; ++i) {
3692                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3693             }
3694
3695             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3696         }
3697         else {
3698             val = superclass.doMethod.call(this, attr, start, end);
3699         }
3700
3701         return val;
3702     };
3703
3704     proto.setRuntimeAttribute = function(attr) {
3705         superclass.setRuntimeAttribute.call(this, attr);
3706
3707         if (this.patterns.color.test(attr)) {
3708             var attributes = this.attributes;
3709             var start = this.parseColor(this.runtimeAttributes[attr].start);
3710             var end = this.parseColor(this.runtimeAttributes[attr].end);
3711
3712             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3713                 end = this.parseColor(attributes[attr].by);
3714
3715                 for (var i = 0, len = start.length; i < len; ++i) {
3716                     end[i] = start[i] + end[i];
3717                 }
3718             }
3719
3720             this.runtimeAttributes[attr].start = start;
3721             this.runtimeAttributes[attr].end = end;
3722         }
3723     };
3724 })();
3725
3726 /*
3727  * Portions of this file are based on pieces of Yahoo User Interface Library
3728  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3729  * YUI licensed under the BSD License:
3730  * http://developer.yahoo.net/yui/license.txt
3731  * <script type="text/javascript">
3732  *
3733  */
3734 Roo.lib.Easing = {
3735
3736
3737     easeNone: function (t, b, c, d) {
3738         return c * t / d + b;
3739     },
3740
3741
3742     easeIn: function (t, b, c, d) {
3743         return c * (t /= d) * t + b;
3744     },
3745
3746
3747     easeOut: function (t, b, c, d) {
3748         return -c * (t /= d) * (t - 2) + b;
3749     },
3750
3751
3752     easeBoth: function (t, b, c, d) {
3753         if ((t /= d / 2) < 1) {
3754             return c / 2 * t * t + b;
3755         }
3756
3757         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3758     },
3759
3760
3761     easeInStrong: function (t, b, c, d) {
3762         return c * (t /= d) * t * t * t + b;
3763     },
3764
3765
3766     easeOutStrong: function (t, b, c, d) {
3767         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3768     },
3769
3770
3771     easeBothStrong: function (t, b, c, d) {
3772         if ((t /= d / 2) < 1) {
3773             return c / 2 * t * t * t * t + b;
3774         }
3775
3776         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3777     },
3778
3779
3780
3781     elasticIn: function (t, b, c, d, a, p) {
3782         if (t == 0) {
3783             return b;
3784         }
3785         if ((t /= d) == 1) {
3786             return b + c;
3787         }
3788         if (!p) {
3789             p = d * .3;
3790         }
3791
3792         if (!a || a < Math.abs(c)) {
3793             a = c;
3794             var s = p / 4;
3795         }
3796         else {
3797             var s = p / (2 * Math.PI) * Math.asin(c / a);
3798         }
3799
3800         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3801     },
3802
3803
3804     elasticOut: function (t, b, c, d, a, p) {
3805         if (t == 0) {
3806             return b;
3807         }
3808         if ((t /= d) == 1) {
3809             return b + c;
3810         }
3811         if (!p) {
3812             p = d * .3;
3813         }
3814
3815         if (!a || a < Math.abs(c)) {
3816             a = c;
3817             var s = p / 4;
3818         }
3819         else {
3820             var s = p / (2 * Math.PI) * Math.asin(c / a);
3821         }
3822
3823         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3824     },
3825
3826
3827     elasticBoth: function (t, b, c, d, a, p) {
3828         if (t == 0) {
3829             return b;
3830         }
3831
3832         if ((t /= d / 2) == 2) {
3833             return b + c;
3834         }
3835
3836         if (!p) {
3837             p = d * (.3 * 1.5);
3838         }
3839
3840         if (!a || a < Math.abs(c)) {
3841             a = c;
3842             var s = p / 4;
3843         }
3844         else {
3845             var s = p / (2 * Math.PI) * Math.asin(c / a);
3846         }
3847
3848         if (t < 1) {
3849             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3850                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3851         }
3852         return a * Math.pow(2, -10 * (t -= 1)) *
3853                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3854     },
3855
3856
3857
3858     backIn: function (t, b, c, d, s) {
3859         if (typeof s == 'undefined') {
3860             s = 1.70158;
3861         }
3862         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3863     },
3864
3865
3866     backOut: function (t, b, c, d, s) {
3867         if (typeof s == 'undefined') {
3868             s = 1.70158;
3869         }
3870         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3871     },
3872
3873
3874     backBoth: function (t, b, c, d, s) {
3875         if (typeof s == 'undefined') {
3876             s = 1.70158;
3877         }
3878
3879         if ((t /= d / 2 ) < 1) {
3880             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3881         }
3882         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3883     },
3884
3885
3886     bounceIn: function (t, b, c, d) {
3887         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3888     },
3889
3890
3891     bounceOut: function (t, b, c, d) {
3892         if ((t /= d) < (1 / 2.75)) {
3893             return c * (7.5625 * t * t) + b;
3894         } else if (t < (2 / 2.75)) {
3895             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3896         } else if (t < (2.5 / 2.75)) {
3897             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3898         }
3899         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3900     },
3901
3902
3903     bounceBoth: function (t, b, c, d) {
3904         if (t < d / 2) {
3905             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3906         }
3907         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3908     }
3909 };/*
3910  * Portions of this file are based on pieces of Yahoo User Interface Library
3911  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3912  * YUI licensed under the BSD License:
3913  * http://developer.yahoo.net/yui/license.txt
3914  * <script type="text/javascript">
3915  *
3916  */
3917     (function() {
3918         Roo.lib.Motion = function(el, attributes, duration, method) {
3919             if (el) {
3920                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3921             }
3922         };
3923
3924         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3925
3926
3927         var Y = Roo.lib;
3928         var superclass = Y.Motion.superclass;
3929         var proto = Y.Motion.prototype;
3930
3931         proto.toString = function() {
3932             var el = this.getEl();
3933             var id = el.id || el.tagName;
3934             return ("Motion " + id);
3935         };
3936
3937         proto.patterns.points = /^points$/i;
3938
3939         proto.setAttribute = function(attr, val, unit) {
3940             if (this.patterns.points.test(attr)) {
3941                 unit = unit || 'px';
3942                 superclass.setAttribute.call(this, 'left', val[0], unit);
3943                 superclass.setAttribute.call(this, 'top', val[1], unit);
3944             } else {
3945                 superclass.setAttribute.call(this, attr, val, unit);
3946             }
3947         };
3948
3949         proto.getAttribute = function(attr) {
3950             if (this.patterns.points.test(attr)) {
3951                 var val = [
3952                         superclass.getAttribute.call(this, 'left'),
3953                         superclass.getAttribute.call(this, 'top')
3954                         ];
3955             } else {
3956                 val = superclass.getAttribute.call(this, attr);
3957             }
3958
3959             return val;
3960         };
3961
3962         proto.doMethod = function(attr, start, end) {
3963             var val = null;
3964
3965             if (this.patterns.points.test(attr)) {
3966                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3967                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3968             } else {
3969                 val = superclass.doMethod.call(this, attr, start, end);
3970             }
3971             return val;
3972         };
3973
3974         proto.setRuntimeAttribute = function(attr) {
3975             if (this.patterns.points.test(attr)) {
3976                 var el = this.getEl();
3977                 var attributes = this.attributes;
3978                 var start;
3979                 var control = attributes['points']['control'] || [];
3980                 var end;
3981                 var i, len;
3982
3983                 if (control.length > 0 && !(control[0] instanceof Array)) {
3984                     control = [control];
3985                 } else {
3986                     var tmp = [];
3987                     for (i = 0,len = control.length; i < len; ++i) {
3988                         tmp[i] = control[i];
3989                     }
3990                     control = tmp;
3991                 }
3992
3993                 Roo.fly(el).position();
3994
3995                 if (isset(attributes['points']['from'])) {
3996                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3997                 }
3998                 else {
3999                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4000                 }
4001
4002                 start = this.getAttribute('points');
4003
4004
4005                 if (isset(attributes['points']['to'])) {
4006                     end = translateValues.call(this, attributes['points']['to'], start);
4007
4008                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009                     for (i = 0,len = control.length; i < len; ++i) {
4010                         control[i] = translateValues.call(this, control[i], start);
4011                     }
4012
4013
4014                 } else if (isset(attributes['points']['by'])) {
4015                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4016
4017                     for (i = 0,len = control.length; i < len; ++i) {
4018                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4019                     }
4020                 }
4021
4022                 this.runtimeAttributes[attr] = [start];
4023
4024                 if (control.length > 0) {
4025                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4026                 }
4027
4028                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4029             }
4030             else {
4031                 superclass.setRuntimeAttribute.call(this, attr);
4032             }
4033         };
4034
4035         var translateValues = function(val, start) {
4036             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4037             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4038
4039             return val;
4040         };
4041
4042         var isset = function(prop) {
4043             return (typeof prop !== 'undefined');
4044         };
4045     })();
4046 /*
4047  * Portions of this file are based on pieces of Yahoo User Interface Library
4048  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4049  * YUI licensed under the BSD License:
4050  * http://developer.yahoo.net/yui/license.txt
4051  * <script type="text/javascript">
4052  *
4053  */
4054     (function() {
4055         Roo.lib.Scroll = function(el, attributes, duration, method) {
4056             if (el) {
4057                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4058             }
4059         };
4060
4061         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4062
4063
4064         var Y = Roo.lib;
4065         var superclass = Y.Scroll.superclass;
4066         var proto = Y.Scroll.prototype;
4067
4068         proto.toString = function() {
4069             var el = this.getEl();
4070             var id = el.id || el.tagName;
4071             return ("Scroll " + id);
4072         };
4073
4074         proto.doMethod = function(attr, start, end) {
4075             var val = null;
4076
4077             if (attr == 'scroll') {
4078                 val = [
4079                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4080                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4081                         ];
4082
4083             } else {
4084                 val = superclass.doMethod.call(this, attr, start, end);
4085             }
4086             return val;
4087         };
4088
4089         proto.getAttribute = function(attr) {
4090             var val = null;
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 val = [ el.scrollLeft, el.scrollTop ];
4095             } else {
4096                 val = superclass.getAttribute.call(this, attr);
4097             }
4098
4099             return val;
4100         };
4101
4102         proto.setAttribute = function(attr, val, unit) {
4103             var el = this.getEl();
4104
4105             if (attr == 'scroll') {
4106                 el.scrollLeft = val[0];
4107                 el.scrollTop = val[1];
4108             } else {
4109                 superclass.setAttribute.call(this, attr, val, unit);
4110             }
4111         };
4112     })();
4113 /*
4114  * Based on:
4115  * Ext JS Library 1.1.1
4116  * Copyright(c) 2006-2007, Ext JS, LLC.
4117  *
4118  * Originally Released Under LGPL - original licence link has changed is not relivant.
4119  *
4120  * Fork - LGPL
4121  * <script type="text/javascript">
4122  */
4123
4124
4125 // nasty IE9 hack - what a pile of crap that is..
4126
4127  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4128     Range.prototype.createContextualFragment = function (html) {
4129         var doc = window.document;
4130         var container = doc.createElement("div");
4131         container.innerHTML = html;
4132         var frag = doc.createDocumentFragment(), n;
4133         while ((n = container.firstChild)) {
4134             frag.appendChild(n);
4135         }
4136         return frag;
4137     };
4138 }
4139
4140 /**
4141  * @class Roo.DomHelper
4142  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4143  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4144  * @singleton
4145  */
4146 Roo.DomHelper = function(){
4147     var tempTableEl = null;
4148     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4149     var tableRe = /^table|tbody|tr|td$/i;
4150     var xmlns = {};
4151     // build as innerHTML where available
4152     /** @ignore */
4153     var createHtml = function(o){
4154         if(typeof o == 'string'){
4155             return o;
4156         }
4157         var b = "";
4158         if(!o.tag){
4159             o.tag = "div";
4160         }
4161         b += "<" + o.tag;
4162         for(var attr in o){
4163             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4164             if(attr == "style"){
4165                 var s = o["style"];
4166                 if(typeof s == "function"){
4167                     s = s.call();
4168                 }
4169                 if(typeof s == "string"){
4170                     b += ' style="' + s + '"';
4171                 }else if(typeof s == "object"){
4172                     b += ' style="';
4173                     for(var key in s){
4174                         if(typeof s[key] != "function"){
4175                             b += key + ":" + s[key] + ";";
4176                         }
4177                     }
4178                     b += '"';
4179                 }
4180             }else{
4181                 if(attr == "cls"){
4182                     b += ' class="' + o["cls"] + '"';
4183                 }else if(attr == "htmlFor"){
4184                     b += ' for="' + o["htmlFor"] + '"';
4185                 }else{
4186                     b += " " + attr + '="' + o[attr] + '"';
4187                 }
4188             }
4189         }
4190         if(emptyTags.test(o.tag)){
4191             b += "/>";
4192         }else{
4193             b += ">";
4194             var cn = o.children || o.cn;
4195             if(cn){
4196                 //http://bugs.kde.org/show_bug.cgi?id=71506
4197                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                     for(var i = 0, len = cn.length; i < len; i++) {
4199                         b += createHtml(cn[i], b);
4200                     }
4201                 }else{
4202                     b += createHtml(cn, b);
4203                 }
4204             }
4205             if(o.html){
4206                 b += o.html;
4207             }
4208             b += "</" + o.tag + ">";
4209         }
4210         return b;
4211     };
4212
4213     // build as dom
4214     /** @ignore */
4215     var createDom = function(o, parentNode){
4216          
4217         // defininition craeted..
4218         var ns = false;
4219         if (o.ns && o.ns != 'html') {
4220                
4221             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4222                 xmlns[o.ns] = o.xmlns;
4223                 ns = o.xmlns;
4224             }
4225             if (typeof(xmlns[o.ns]) == 'undefined') {
4226                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4227             }
4228             ns = xmlns[o.ns];
4229         }
4230         
4231         
4232         if (typeof(o) == 'string') {
4233             return parentNode.appendChild(document.createTextNode(o));
4234         }
4235         o.tag = o.tag || div;
4236         if (o.ns && Roo.isIE) {
4237             ns = false;
4238             o.tag = o.ns + ':' + o.tag;
4239             
4240         }
4241         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4242         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4243         for(var attr in o){
4244             
4245             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4246                     attr == "style" || typeof o[attr] == "function") { continue; }
4247                     
4248             if(attr=="cls" && Roo.isIE){
4249                 el.className = o["cls"];
4250             }else{
4251                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4252                 else { 
4253                     el[attr] = o[attr];
4254                 }
4255             }
4256         }
4257         Roo.DomHelper.applyStyles(el, o.style);
4258         var cn = o.children || o.cn;
4259         if(cn){
4260             //http://bugs.kde.org/show_bug.cgi?id=71506
4261              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4262                 for(var i = 0, len = cn.length; i < len; i++) {
4263                     createDom(cn[i], el);
4264                 }
4265             }else{
4266                 createDom(cn, el);
4267             }
4268         }
4269         if(o.html){
4270             el.innerHTML = o.html;
4271         }
4272         if(parentNode){
4273            parentNode.appendChild(el);
4274         }
4275         return el;
4276     };
4277
4278     var ieTable = function(depth, s, h, e){
4279         tempTableEl.innerHTML = [s, h, e].join('');
4280         var i = -1, el = tempTableEl;
4281         while(++i < depth){
4282             el = el.firstChild;
4283         }
4284         return el;
4285     };
4286
4287     // kill repeat to save bytes
4288     var ts = '<table>',
4289         te = '</table>',
4290         tbs = ts+'<tbody>',
4291         tbe = '</tbody>'+te,
4292         trs = tbs + '<tr>',
4293         tre = '</tr>'+tbe;
4294
4295     /**
4296      * @ignore
4297      * Nasty code for IE's broken table implementation
4298      */
4299     var insertIntoTable = function(tag, where, el, html){
4300         if(!tempTableEl){
4301             tempTableEl = document.createElement('div');
4302         }
4303         var node;
4304         var before = null;
4305         if(tag == 'td'){
4306             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4307                 return;
4308             }
4309             if(where == 'beforebegin'){
4310                 before = el;
4311                 el = el.parentNode;
4312             } else{
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315             }
4316             node = ieTable(4, trs, html, tre);
4317         }
4318         else if(tag == 'tr'){
4319             if(where == 'beforebegin'){
4320                 before = el;
4321                 el = el.parentNode;
4322                 node = ieTable(3, tbs, html, tbe);
4323             } else if(where == 'afterend'){
4324                 before = el.nextSibling;
4325                 el = el.parentNode;
4326                 node = ieTable(3, tbs, html, tbe);
4327             } else{ // INTO a TR
4328                 if(where == 'afterbegin'){
4329                     before = el.firstChild;
4330                 }
4331                 node = ieTable(4, trs, html, tre);
4332             }
4333         } else if(tag == 'tbody'){
4334             if(where == 'beforebegin'){
4335                 before = el;
4336                 el = el.parentNode;
4337                 node = ieTable(2, ts, html, te);
4338             } else if(where == 'afterend'){
4339                 before = el.nextSibling;
4340                 el = el.parentNode;
4341                 node = ieTable(2, ts, html, te);
4342             } else{
4343                 if(where == 'afterbegin'){
4344                     before = el.firstChild;
4345                 }
4346                 node = ieTable(3, tbs, html, tbe);
4347             }
4348         } else{ // TABLE
4349             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4350                 return;
4351             }
4352             if(where == 'afterbegin'){
4353                 before = el.firstChild;
4354             }
4355             node = ieTable(2, ts, html, te);
4356         }
4357         el.insertBefore(node, before);
4358         return node;
4359     };
4360
4361     return {
4362     /** True to force the use of DOM instead of html fragments @type Boolean */
4363     useDom : false,
4364
4365     /**
4366      * Returns the markup for the passed Element(s) config
4367      * @param {Object} o The Dom object spec (and children)
4368      * @return {String}
4369      */
4370     markup : function(o){
4371         return createHtml(o);
4372     },
4373
4374     /**
4375      * Applies a style specification to an element
4376      * @param {String/HTMLElement} el The element to apply styles to
4377      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4378      * a function which returns such a specification.
4379      */
4380     applyStyles : function(el, styles){
4381         if(styles){
4382            el = Roo.fly(el);
4383            if(typeof styles == "string"){
4384                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4385                var matches;
4386                while ((matches = re.exec(styles)) != null){
4387                    el.setStyle(matches[1], matches[2]);
4388                }
4389            }else if (typeof styles == "object"){
4390                for (var style in styles){
4391                   el.setStyle(style, styles[style]);
4392                }
4393            }else if (typeof styles == "function"){
4394                 Roo.DomHelper.applyStyles(el, styles.call());
4395            }
4396         }
4397     },
4398
4399     /**
4400      * Inserts an HTML fragment into the Dom
4401      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4402      * @param {HTMLElement} el The context element
4403      * @param {String} html The HTML fragmenet
4404      * @return {HTMLElement} The new node
4405      */
4406     insertHtml : function(where, el, html){
4407         where = where.toLowerCase();
4408         if(el.insertAdjacentHTML){
4409             if(tableRe.test(el.tagName)){
4410                 var rs;
4411                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4412                     return rs;
4413                 }
4414             }
4415             switch(where){
4416                 case "beforebegin":
4417                     el.insertAdjacentHTML('BeforeBegin', html);
4418                     return el.previousSibling;
4419                 case "afterbegin":
4420                     el.insertAdjacentHTML('AfterBegin', html);
4421                     return el.firstChild;
4422                 case "beforeend":
4423                     el.insertAdjacentHTML('BeforeEnd', html);
4424                     return el.lastChild;
4425                 case "afterend":
4426                     el.insertAdjacentHTML('AfterEnd', html);
4427                     return el.nextSibling;
4428             }
4429             throw 'Illegal insertion point -> "' + where + '"';
4430         }
4431         var range = el.ownerDocument.createRange();
4432         var frag;
4433         switch(where){
4434              case "beforebegin":
4435                 range.setStartBefore(el);
4436                 frag = range.createContextualFragment(html);
4437                 el.parentNode.insertBefore(frag, el);
4438                 return el.previousSibling;
4439              case "afterbegin":
4440                 if(el.firstChild){
4441                     range.setStartBefore(el.firstChild);
4442                     frag = range.createContextualFragment(html);
4443                     el.insertBefore(frag, el.firstChild);
4444                     return el.firstChild;
4445                 }else{
4446                     el.innerHTML = html;
4447                     return el.firstChild;
4448                 }
4449             case "beforeend":
4450                 if(el.lastChild){
4451                     range.setStartAfter(el.lastChild);
4452                     frag = range.createContextualFragment(html);
4453                     el.appendChild(frag);
4454                     return el.lastChild;
4455                 }else{
4456                     el.innerHTML = html;
4457                     return el.lastChild;
4458                 }
4459             case "afterend":
4460                 range.setStartAfter(el);
4461                 frag = range.createContextualFragment(html);
4462                 el.parentNode.insertBefore(frag, el.nextSibling);
4463                 return el.nextSibling;
4464             }
4465             throw 'Illegal insertion point -> "' + where + '"';
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them before el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertBefore : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "beforeBegin");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them after el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object} o The Dom object spec (and children)
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertAfter : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4488     },
4489
4490     /**
4491      * Creates new Dom element(s) and inserts them as the first child of el
4492      * @param {String/HTMLElement/Element} el The context element
4493      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4494      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4495      * @return {HTMLElement/Roo.Element} The new node
4496      */
4497     insertFirst : function(el, o, returnElement){
4498         return this.doInsert(el, o, returnElement, "afterBegin");
4499     },
4500
4501     // private
4502     doInsert : function(el, o, returnElement, pos, sibling){
4503         el = Roo.getDom(el);
4504         var newNode;
4505         if(this.useDom || o.ns){
4506             newNode = createDom(o, null);
4507             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4508         }else{
4509             var html = createHtml(o);
4510             newNode = this.insertHtml(pos, el, html);
4511         }
4512         return returnElement ? Roo.get(newNode, true) : newNode;
4513     },
4514
4515     /**
4516      * Creates new Dom element(s) and appends them to el
4517      * @param {String/HTMLElement/Element} el The context element
4518      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4519      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4520      * @return {HTMLElement/Roo.Element} The new node
4521      */
4522     append : function(el, o, returnElement){
4523         el = Roo.getDom(el);
4524         var newNode;
4525         if(this.useDom || o.ns){
4526             newNode = createDom(o, null);
4527             el.appendChild(newNode);
4528         }else{
4529             var html = createHtml(o);
4530             newNode = this.insertHtml("beforeEnd", el, html);
4531         }
4532         return returnElement ? Roo.get(newNode, true) : newNode;
4533     },
4534
4535     /**
4536      * Creates new Dom element(s) and overwrites the contents of el with them
4537      * @param {String/HTMLElement/Element} el The context element
4538      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4539      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4540      * @return {HTMLElement/Roo.Element} The new node
4541      */
4542     overwrite : function(el, o, returnElement){
4543         el = Roo.getDom(el);
4544         if (o.ns) {
4545           
4546             while (el.childNodes.length) {
4547                 el.removeChild(el.firstChild);
4548             }
4549             createDom(o, el);
4550         } else {
4551             el.innerHTML = createHtml(o);   
4552         }
4553         
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     },
4556
4557     /**
4558      * Creates a new Roo.DomHelper.Template from the Dom object spec
4559      * @param {Object} o The Dom object spec (and children)
4560      * @return {Roo.DomHelper.Template} The new template
4561      */
4562     createTemplate : function(o){
4563         var html = createHtml(o);
4564         return new Roo.Template(html);
4565     }
4566     };
4567 }();
4568 /*
4569  * Based on:
4570  * Ext JS Library 1.1.1
4571  * Copyright(c) 2006-2007, Ext JS, LLC.
4572  *
4573  * Originally Released Under LGPL - original licence link has changed is not relivant.
4574  *
4575  * Fork - LGPL
4576  * <script type="text/javascript">
4577  */
4578  
4579 /**
4580 * @class Roo.Template
4581 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4582 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4583 * Usage:
4584 <pre><code>
4585 var t = new Roo.Template({
4586     html :  '&lt;div name="{id}"&gt;' + 
4587         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4588         '&lt;/div&gt;',
4589     myformat: function (value, allValues) {
4590         return 'XX' + value;
4591     }
4592 });
4593 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4594 </code></pre>
4595 * For more information see this blog post with examples:
4596 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4597      - Create Elements using DOM, HTML fragments and Templates</a>. 
4598 * @constructor
4599 * @param {Object} cfg - Configuration object.
4600 */
4601 Roo.Template = function(cfg){
4602     // BC!
4603     if(cfg instanceof Array){
4604         cfg = cfg.join("");
4605     }else if(arguments.length > 1){
4606         cfg = Array.prototype.join.call(arguments, "");
4607     }
4608     
4609     
4610     if (typeof(cfg) == 'object') {
4611         Roo.apply(this,cfg)
4612     } else {
4613         // bc
4614         this.html = cfg;
4615     }
4616     if (this.url) {
4617         this.load();
4618     }
4619     
4620 };
4621 Roo.Template.prototype = {
4622     
4623     /**
4624      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4625      *                    it should be fixed so that template is observable...
4626      */
4627     url : false,
4628     /**
4629      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4630      */
4631     html : '',
4632     /**
4633      * Returns an HTML fragment of this template with the specified values applied.
4634      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4635      * @return {String} The HTML fragment
4636      */
4637     applyTemplate : function(values){
4638         try {
4639            
4640             if(this.compiled){
4641                 return this.compiled(values);
4642             }
4643             var useF = this.disableFormats !== true;
4644             var fm = Roo.util.Format, tpl = this;
4645             var fn = function(m, name, format, args){
4646                 if(format && useF){
4647                     if(format.substr(0, 5) == "this."){
4648                         return tpl.call(format.substr(5), values[name], values);
4649                     }else{
4650                         if(args){
4651                             // quoted values are required for strings in compiled templates, 
4652                             // but for non compiled we need to strip them
4653                             // quoted reversed for jsmin
4654                             var re = /^\s*['"](.*)["']\s*$/;
4655                             args = args.split(',');
4656                             for(var i = 0, len = args.length; i < len; i++){
4657                                 args[i] = args[i].replace(re, "$1");
4658                             }
4659                             args = [values[name]].concat(args);
4660                         }else{
4661                             args = [values[name]];
4662                         }
4663                         return fm[format].apply(fm, args);
4664                     }
4665                 }else{
4666                     return values[name] !== undefined ? values[name] : "";
4667                 }
4668             };
4669             return this.html.replace(this.re, fn);
4670         } catch (e) {
4671             Roo.log(e);
4672             throw e;
4673         }
4674          
4675     },
4676     
4677     loading : false,
4678       
4679     load : function ()
4680     {
4681          
4682         if (this.loading) {
4683             return;
4684         }
4685         var _t = this;
4686         
4687         this.loading = true;
4688         this.compiled = false;
4689         
4690         var cx = new Roo.data.Connection();
4691         cx.request({
4692             url : this.url,
4693             method : 'GET',
4694             success : function (response) {
4695                 _t.loading = false;
4696                 _t.html = response.responseText;
4697                 _t.url = false;
4698                 _t.compile();
4699              },
4700             failure : function(response) {
4701                 Roo.log("Template failed to load from " + _t.url);
4702                 _t.loading = false;
4703             }
4704         });
4705     },
4706
4707     /**
4708      * Sets the HTML used as the template and optionally compiles it.
4709      * @param {String} html
4710      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4711      * @return {Roo.Template} this
4712      */
4713     set : function(html, compile){
4714         this.html = html;
4715         this.compiled = null;
4716         if(compile){
4717             this.compile();
4718         }
4719         return this;
4720     },
4721     
4722     /**
4723      * True to disable format functions (defaults to false)
4724      * @type Boolean
4725      */
4726     disableFormats : false,
4727     
4728     /**
4729     * The regular expression used to match template variables 
4730     * @type RegExp
4731     * @property 
4732     */
4733     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4734     
4735     /**
4736      * Compiles the template into an internal function, eliminating the RegEx overhead.
4737      * @return {Roo.Template} this
4738      */
4739     compile : function(){
4740         var fm = Roo.util.Format;
4741         var useF = this.disableFormats !== true;
4742         var sep = Roo.isGecko ? "+" : ",";
4743         var fn = function(m, name, format, args){
4744             if(format && useF){
4745                 args = args ? ',' + args : "";
4746                 if(format.substr(0, 5) != "this."){
4747                     format = "fm." + format + '(';
4748                 }else{
4749                     format = 'this.call("'+ format.substr(5) + '", ';
4750                     args = ", values";
4751                 }
4752             }else{
4753                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4754             }
4755             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4756         };
4757         var body;
4758         // branched to use + in gecko and [].join() in others
4759         if(Roo.isGecko){
4760             body = "this.compiled = function(values){ return '" +
4761                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4762                     "';};";
4763         }else{
4764             body = ["this.compiled = function(values){ return ['"];
4765             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4766             body.push("'].join('');};");
4767             body = body.join('');
4768         }
4769         /**
4770          * eval:var:values
4771          * eval:var:fm
4772          */
4773         eval(body);
4774         return this;
4775     },
4776     
4777     // private function used to call members
4778     call : function(fnName, value, allValues){
4779         return this[fnName](value, allValues);
4780     },
4781     
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertFirst: function(el, values, returnElement){
4790         return this.doInsert('afterBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) before el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertBefore: function(el, values, returnElement){
4801         return this.doInsert('beforeBegin', el, values, returnElement);
4802     },
4803
4804     /**
4805      * Applies the supplied values to the template and inserts the new node(s) after el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     insertAfter : function(el, values, returnElement){
4812         return this.doInsert('afterEnd', el, values, returnElement);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and appends the new node(s) to el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     append : function(el, values, returnElement){
4823         return this.doInsert('beforeEnd', el, values, returnElement);
4824     },
4825
4826     doInsert : function(where, el, values, returnEl){
4827         el = Roo.getDom(el);
4828         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4829         return returnEl ? Roo.get(newNode, true) : newNode;
4830     },
4831
4832     /**
4833      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4834      * @param {String/HTMLElement/Roo.Element} el The context element
4835      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4836      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4837      * @return {HTMLElement/Roo.Element} The new node or Element
4838      */
4839     overwrite : function(el, values, returnElement){
4840         el = Roo.getDom(el);
4841         el.innerHTML = this.applyTemplate(values);
4842         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4843     }
4844 };
4845 /**
4846  * Alias for {@link #applyTemplate}
4847  * @method
4848  */
4849 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4850
4851 // backwards compat
4852 Roo.DomHelper.Template = Roo.Template;
4853
4854 /**
4855  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4856  * @param {String/HTMLElement} el A DOM element or its id
4857  * @returns {Roo.Template} The created template
4858  * @static
4859  */
4860 Roo.Template.from = function(el){
4861     el = Roo.getDom(el);
4862     return new Roo.Template(el.value || el.innerHTML);
4863 };/*
4864  * Based on:
4865  * Ext JS Library 1.1.1
4866  * Copyright(c) 2006-2007, Ext JS, LLC.
4867  *
4868  * Originally Released Under LGPL - original licence link has changed is not relivant.
4869  *
4870  * Fork - LGPL
4871  * <script type="text/javascript">
4872  */
4873  
4874
4875 /*
4876  * This is code is also distributed under MIT license for use
4877  * with jQuery and prototype JavaScript libraries.
4878  */
4879 /**
4880  * @class Roo.DomQuery
4881 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4882 <p>
4883 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4884
4885 <p>
4886 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4887 </p>
4888 <h4>Element Selectors:</h4>
4889 <ul class="list">
4890     <li> <b>*</b> any element</li>
4891     <li> <b>E</b> an element with the tag E</li>
4892     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4893     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4894     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4895     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4896 </ul>
4897 <h4>Attribute Selectors:</h4>
4898 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4899 <ul class="list">
4900     <li> <b>E[foo]</b> has an attribute "foo"</li>
4901     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4902     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4903     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4904     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4905     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4906     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4907 </ul>
4908 <h4>Pseudo Classes:</h4>
4909 <ul class="list">
4910     <li> <b>E:first-child</b> E is the first child of its parent</li>
4911     <li> <b>E:last-child</b> E is the last child of its parent</li>
4912     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4913     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4914     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4915     <li> <b>E:only-child</b> E is the only child of its parent</li>
4916     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4917     <li> <b>E:first</b> the first E in the resultset</li>
4918     <li> <b>E:last</b> the last E in the resultset</li>
4919     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4920     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4921     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4922     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4923     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4924     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4925     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4926     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4927     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4928 </ul>
4929 <h4>CSS Value Selectors:</h4>
4930 <ul class="list">
4931     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4932     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4933     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4934     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4935     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4936     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4937 </ul>
4938  * @singleton
4939  */
4940 Roo.DomQuery = function(){
4941     var cache = {}, simpleCache = {}, valueCache = {};
4942     var nonSpace = /\S/;
4943     var trimRe = /^\s+|\s+$/g;
4944     var tplRe = /\{(\d+)\}/g;
4945     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4946     var tagTokenRe = /^(#)?([\w-\*]+)/;
4947     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4948
4949     function child(p, index){
4950         var i = 0;
4951         var n = p.firstChild;
4952         while(n){
4953             if(n.nodeType == 1){
4954                if(++i == index){
4955                    return n;
4956                }
4957             }
4958             n = n.nextSibling;
4959         }
4960         return null;
4961     };
4962
4963     function next(n){
4964         while((n = n.nextSibling) && n.nodeType != 1);
4965         return n;
4966     };
4967
4968     function prev(n){
4969         while((n = n.previousSibling) && n.nodeType != 1);
4970         return n;
4971     };
4972
4973     function children(d){
4974         var n = d.firstChild, ni = -1;
4975             while(n){
4976                 var nx = n.nextSibling;
4977                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4978                     d.removeChild(n);
4979                 }else{
4980                     n.nodeIndex = ++ni;
4981                 }
4982                 n = nx;
4983             }
4984             return this;
4985         };
4986
4987     function byClassName(c, a, v){
4988         if(!v){
4989             return c;
4990         }
4991         var r = [], ri = -1, cn;
4992         for(var i = 0, ci; ci = c[i]; i++){
4993             if((' '+ci.className+' ').indexOf(v) != -1){
4994                 r[++ri] = ci;
4995             }
4996         }
4997         return r;
4998     };
4999
5000     function attrValue(n, attr){
5001         if(!n.tagName && typeof n.length != "undefined"){
5002             n = n[0];
5003         }
5004         if(!n){
5005             return null;
5006         }
5007         if(attr == "for"){
5008             return n.htmlFor;
5009         }
5010         if(attr == "class" || attr == "className"){
5011             return n.className;
5012         }
5013         return n.getAttribute(attr) || n[attr];
5014
5015     };
5016
5017     function getNodes(ns, mode, tagName){
5018         var result = [], ri = -1, cs;
5019         if(!ns){
5020             return result;
5021         }
5022         tagName = tagName || "*";
5023         if(typeof ns.getElementsByTagName != "undefined"){
5024             ns = [ns];
5025         }
5026         if(!mode){
5027             for(var i = 0, ni; ni = ns[i]; i++){
5028                 cs = ni.getElementsByTagName(tagName);
5029                 for(var j = 0, ci; ci = cs[j]; j++){
5030                     result[++ri] = ci;
5031                 }
5032             }
5033         }else if(mode == "/" || mode == ">"){
5034             var utag = tagName.toUpperCase();
5035             for(var i = 0, ni, cn; ni = ns[i]; i++){
5036                 cn = ni.children || ni.childNodes;
5037                 for(var j = 0, cj; cj = cn[j]; j++){
5038                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5039                         result[++ri] = cj;
5040                     }
5041                 }
5042             }
5043         }else if(mode == "+"){
5044             var utag = tagName.toUpperCase();
5045             for(var i = 0, n; n = ns[i]; i++){
5046                 while((n = n.nextSibling) && n.nodeType != 1);
5047                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5048                     result[++ri] = n;
5049                 }
5050             }
5051         }else if(mode == "~"){
5052             for(var i = 0, n; n = ns[i]; i++){
5053                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5054                 if(n){
5055                     result[++ri] = n;
5056                 }
5057             }
5058         }
5059         return result;
5060     };
5061
5062     function concat(a, b){
5063         if(b.slice){
5064             return a.concat(b);
5065         }
5066         for(var i = 0, l = b.length; i < l; i++){
5067             a[a.length] = b[i];
5068         }
5069         return a;
5070     }
5071
5072     function byTag(cs, tagName){
5073         if(cs.tagName || cs == document){
5074             cs = [cs];
5075         }
5076         if(!tagName){
5077             return cs;
5078         }
5079         var r = [], ri = -1;
5080         tagName = tagName.toLowerCase();
5081         for(var i = 0, ci; ci = cs[i]; i++){
5082             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5083                 r[++ri] = ci;
5084             }
5085         }
5086         return r;
5087     };
5088
5089     function byId(cs, attr, id){
5090         if(cs.tagName || cs == document){
5091             cs = [cs];
5092         }
5093         if(!id){
5094             return cs;
5095         }
5096         var r = [], ri = -1;
5097         for(var i = 0,ci; ci = cs[i]; i++){
5098             if(ci && ci.id == id){
5099                 r[++ri] = ci;
5100                 return r;
5101             }
5102         }
5103         return r;
5104     };
5105
5106     function byAttribute(cs, attr, value, op, custom){
5107         var r = [], ri = -1, st = custom=="{";
5108         var f = Roo.DomQuery.operators[op];
5109         for(var i = 0, ci; ci = cs[i]; i++){
5110             var a;
5111             if(st){
5112                 a = Roo.DomQuery.getStyle(ci, attr);
5113             }
5114             else if(attr == "class" || attr == "className"){
5115                 a = ci.className;
5116             }else if(attr == "for"){
5117                 a = ci.htmlFor;
5118             }else if(attr == "href"){
5119                 a = ci.getAttribute("href", 2);
5120             }else{
5121                 a = ci.getAttribute(attr);
5122             }
5123             if((f && f(a, value)) || (!f && a)){
5124                 r[++ri] = ci;
5125             }
5126         }
5127         return r;
5128     };
5129
5130     function byPseudo(cs, name, value){
5131         return Roo.DomQuery.pseudos[name](cs, value);
5132     };
5133
5134     // This is for IE MSXML which does not support expandos.
5135     // IE runs the same speed using setAttribute, however FF slows way down
5136     // and Safari completely fails so they need to continue to use expandos.
5137     var isIE = window.ActiveXObject ? true : false;
5138
5139     // this eval is stop the compressor from
5140     // renaming the variable to something shorter
5141     
5142     /** eval:var:batch */
5143     var batch = 30803; 
5144
5145     var key = 30803;
5146
5147     function nodupIEXml(cs){
5148         var d = ++key;
5149         cs[0].setAttribute("_nodup", d);
5150         var r = [cs[0]];
5151         for(var i = 1, len = cs.length; i < len; i++){
5152             var c = cs[i];
5153             if(!c.getAttribute("_nodup") != d){
5154                 c.setAttribute("_nodup", d);
5155                 r[r.length] = c;
5156             }
5157         }
5158         for(var i = 0, len = cs.length; i < len; i++){
5159             cs[i].removeAttribute("_nodup");
5160         }
5161         return r;
5162     }
5163
5164     function nodup(cs){
5165         if(!cs){
5166             return [];
5167         }
5168         var len = cs.length, c, i, r = cs, cj, ri = -1;
5169         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5170             return cs;
5171         }
5172         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5173             return nodupIEXml(cs);
5174         }
5175         var d = ++key;
5176         cs[0]._nodup = d;
5177         for(i = 1; c = cs[i]; i++){
5178             if(c._nodup != d){
5179                 c._nodup = d;
5180             }else{
5181                 r = [];
5182                 for(var j = 0; j < i; j++){
5183                     r[++ri] = cs[j];
5184                 }
5185                 for(j = i+1; cj = cs[j]; j++){
5186                     if(cj._nodup != d){
5187                         cj._nodup = d;
5188                         r[++ri] = cj;
5189                     }
5190                 }
5191                 return r;
5192             }
5193         }
5194         return r;
5195     }
5196
5197     function quickDiffIEXml(c1, c2){
5198         var d = ++key;
5199         for(var i = 0, len = c1.length; i < len; i++){
5200             c1[i].setAttribute("_qdiff", d);
5201         }
5202         var r = [];
5203         for(var i = 0, len = c2.length; i < len; i++){
5204             if(c2[i].getAttribute("_qdiff") != d){
5205                 r[r.length] = c2[i];
5206             }
5207         }
5208         for(var i = 0, len = c1.length; i < len; i++){
5209            c1[i].removeAttribute("_qdiff");
5210         }
5211         return r;
5212     }
5213
5214     function quickDiff(c1, c2){
5215         var len1 = c1.length;
5216         if(!len1){
5217             return c2;
5218         }
5219         if(isIE && c1[0].selectSingleNode){
5220             return quickDiffIEXml(c1, c2);
5221         }
5222         var d = ++key;
5223         for(var i = 0; i < len1; i++){
5224             c1[i]._qdiff = d;
5225         }
5226         var r = [];
5227         for(var i = 0, len = c2.length; i < len; i++){
5228             if(c2[i]._qdiff != d){
5229                 r[r.length] = c2[i];
5230             }
5231         }
5232         return r;
5233     }
5234
5235     function quickId(ns, mode, root, id){
5236         if(ns == root){
5237            var d = root.ownerDocument || root;
5238            return d.getElementById(id);
5239         }
5240         ns = getNodes(ns, mode, "*");
5241         return byId(ns, null, id);
5242     }
5243
5244     return {
5245         getStyle : function(el, name){
5246             return Roo.fly(el).getStyle(name);
5247         },
5248         /**
5249          * Compiles a selector/xpath query into a reusable function. The returned function
5250          * takes one parameter "root" (optional), which is the context node from where the query should start.
5251          * @param {String} selector The selector/xpath query
5252          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5253          * @return {Function}
5254          */
5255         compile : function(path, type){
5256             type = type || "select";
5257             
5258             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5259             var q = path, mode, lq;
5260             var tk = Roo.DomQuery.matchers;
5261             var tklen = tk.length;
5262             var mm;
5263
5264             // accept leading mode switch
5265             var lmode = q.match(modeRe);
5266             if(lmode && lmode[1]){
5267                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5268                 q = q.replace(lmode[1], "");
5269             }
5270             // strip leading slashes
5271             while(path.substr(0, 1)=="/"){
5272                 path = path.substr(1);
5273             }
5274
5275             while(q && lq != q){
5276                 lq = q;
5277                 var tm = q.match(tagTokenRe);
5278                 if(type == "select"){
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }else if(q.substr(0, 1) != '@'){
5287                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5288                     }
5289                 }else{
5290                     if(tm){
5291                         if(tm[1] == "#"){
5292                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5293                         }else{
5294                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5295                         }
5296                         q = q.replace(tm[0], "");
5297                     }
5298                 }
5299                 while(!(mm = q.match(modeRe))){
5300                     var matched = false;
5301                     for(var j = 0; j < tklen; j++){
5302                         var t = tk[j];
5303                         var m = q.match(t.re);
5304                         if(m){
5305                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5306                                                     return m[i];
5307                                                 });
5308                             q = q.replace(m[0], "");
5309                             matched = true;
5310                             break;
5311                         }
5312                     }
5313                     // prevent infinite loop on bad selector
5314                     if(!matched){
5315                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5316                     }
5317                 }
5318                 if(mm[1]){
5319                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5320                     q = q.replace(mm[1], "");
5321                 }
5322             }
5323             fn[fn.length] = "return nodup(n);\n}";
5324             
5325              /** 
5326               * list of variables that need from compression as they are used by eval.
5327              *  eval:var:batch 
5328              *  eval:var:nodup
5329              *  eval:var:byTag
5330              *  eval:var:ById
5331              *  eval:var:getNodes
5332              *  eval:var:quickId
5333              *  eval:var:mode
5334              *  eval:var:root
5335              *  eval:var:n
5336              *  eval:var:byClassName
5337              *  eval:var:byPseudo
5338              *  eval:var:byAttribute
5339              *  eval:var:attrValue
5340              * 
5341              **/ 
5342             eval(fn.join(""));
5343             return f;
5344         },
5345
5346         /**
5347          * Selects a group of elements.
5348          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5349          * @param {Node} root (optional) The start of the query (defaults to document).
5350          * @return {Array}
5351          */
5352         select : function(path, root, type){
5353             if(!root || root == document){
5354                 root = document;
5355             }
5356             if(typeof root == "string"){
5357                 root = document.getElementById(root);
5358             }
5359             var paths = path.split(",");
5360             var results = [];
5361             for(var i = 0, len = paths.length; i < len; i++){
5362                 var p = paths[i].replace(trimRe, "");
5363                 if(!cache[p]){
5364                     cache[p] = Roo.DomQuery.compile(p);
5365                     if(!cache[p]){
5366                         throw p + " is not a valid selector";
5367                     }
5368                 }
5369                 var result = cache[p](root);
5370                 if(result && result != document){
5371                     results = results.concat(result);
5372                 }
5373             }
5374             if(paths.length > 1){
5375                 return nodup(results);
5376             }
5377             return results;
5378         },
5379
5380         /**
5381          * Selects a single element.
5382          * @param {String} selector The selector/xpath query
5383          * @param {Node} root (optional) The start of the query (defaults to document).
5384          * @return {Element}
5385          */
5386         selectNode : function(path, root){
5387             return Roo.DomQuery.select(path, root)[0];
5388         },
5389
5390         /**
5391          * Selects the value of a node, optionally replacing null with the defaultValue.
5392          * @param {String} selector The selector/xpath query
5393          * @param {Node} root (optional) The start of the query (defaults to document).
5394          * @param {String} defaultValue
5395          */
5396         selectValue : function(path, root, defaultValue){
5397             path = path.replace(trimRe, "");
5398             if(!valueCache[path]){
5399                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5400             }
5401             var n = valueCache[path](root);
5402             n = n[0] ? n[0] : n;
5403             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5404             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5405         },
5406
5407         /**
5408          * Selects the value of a node, parsing integers and floats.
5409          * @param {String} selector The selector/xpath query
5410          * @param {Node} root (optional) The start of the query (defaults to document).
5411          * @param {Number} defaultValue
5412          * @return {Number}
5413          */
5414         selectNumber : function(path, root, defaultValue){
5415             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5416             return parseFloat(v);
5417         },
5418
5419         /**
5420          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5421          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5422          * @param {String} selector The simple selector to test
5423          * @return {Boolean}
5424          */
5425         is : function(el, ss){
5426             if(typeof el == "string"){
5427                 el = document.getElementById(el);
5428             }
5429             var isArray = (el instanceof Array);
5430             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5431             return isArray ? (result.length == el.length) : (result.length > 0);
5432         },
5433
5434         /**
5435          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5436          * @param {Array} el An array of elements to filter
5437          * @param {String} selector The simple selector to test
5438          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5439          * the selector instead of the ones that match
5440          * @return {Array}
5441          */
5442         filter : function(els, ss, nonMatches){
5443             ss = ss.replace(trimRe, "");
5444             if(!simpleCache[ss]){
5445                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5446             }
5447             var result = simpleCache[ss](els);
5448             return nonMatches ? quickDiff(result, els) : result;
5449         },
5450
5451         /**
5452          * Collection of matching regular expressions and code snippets.
5453          */
5454         matchers : [{
5455                 re: /^\.([\w-]+)/,
5456                 select: 'n = byClassName(n, null, " {1} ");'
5457             }, {
5458                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5459                 select: 'n = byPseudo(n, "{1}", "{2}");'
5460             },{
5461                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5462                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5463             }, {
5464                 re: /^#([\w-]+)/,
5465                 select: 'n = byId(n, null, "{1}");'
5466             },{
5467                 re: /^@([\w-]+)/,
5468                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5469             }
5470         ],
5471
5472         /**
5473          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5474          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5475          */
5476         operators : {
5477             "=" : function(a, v){
5478                 return a == v;
5479             },
5480             "!=" : function(a, v){
5481                 return a != v;
5482             },
5483             "^=" : function(a, v){
5484                 return a && a.substr(0, v.length) == v;
5485             },
5486             "$=" : function(a, v){
5487                 return a && a.substr(a.length-v.length) == v;
5488             },
5489             "*=" : function(a, v){
5490                 return a && a.indexOf(v) !== -1;
5491             },
5492             "%=" : function(a, v){
5493                 return (a % v) == 0;
5494             },
5495             "|=" : function(a, v){
5496                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5497             },
5498             "~=" : function(a, v){
5499                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5500             }
5501         },
5502
5503         /**
5504          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5505          * and the argument (if any) supplied in the selector.
5506          */
5507         pseudos : {
5508             "first-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.previousSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "last-child" : function(c){
5520                 var r = [], ri = -1, n;
5521                 for(var i = 0, ci; ci = n = c[i]; i++){
5522                     while((n = n.nextSibling) && n.nodeType != 1);
5523                     if(!n){
5524                         r[++ri] = ci;
5525                     }
5526                 }
5527                 return r;
5528             },
5529
5530             "nth-child" : function(c, a) {
5531                 var r = [], ri = -1;
5532                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5533                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5534                 for(var i = 0, n; n = c[i]; i++){
5535                     var pn = n.parentNode;
5536                     if (batch != pn._batch) {
5537                         var j = 0;
5538                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5539                             if(cn.nodeType == 1){
5540                                cn.nodeIndex = ++j;
5541                             }
5542                         }
5543                         pn._batch = batch;
5544                     }
5545                     if (f == 1) {
5546                         if (l == 0 || n.nodeIndex == l){
5547                             r[++ri] = n;
5548                         }
5549                     } else if ((n.nodeIndex + l) % f == 0){
5550                         r[++ri] = n;
5551                     }
5552                 }
5553
5554                 return r;
5555             },
5556
5557             "only-child" : function(c){
5558                 var r = [], ri = -1;;
5559                 for(var i = 0, ci; ci = c[i]; i++){
5560                     if(!prev(ci) && !next(ci)){
5561                         r[++ri] = ci;
5562                     }
5563                 }
5564                 return r;
5565             },
5566
5567             "empty" : function(c){
5568                 var r = [], ri = -1;
5569                 for(var i = 0, ci; ci = c[i]; i++){
5570                     var cns = ci.childNodes, j = 0, cn, empty = true;
5571                     while(cn = cns[j]){
5572                         ++j;
5573                         if(cn.nodeType == 1 || cn.nodeType == 3){
5574                             empty = false;
5575                             break;
5576                         }
5577                     }
5578                     if(empty){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "contains" : function(c, v){
5586                 var r = [], ri = -1;
5587                 for(var i = 0, ci; ci = c[i]; i++){
5588                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "nodeValue" : function(c, v){
5596                 var r = [], ri = -1;
5597                 for(var i = 0, ci; ci = c[i]; i++){
5598                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5599                         r[++ri] = ci;
5600                     }
5601                 }
5602                 return r;
5603             },
5604
5605             "checked" : function(c){
5606                 var r = [], ri = -1;
5607                 for(var i = 0, ci; ci = c[i]; i++){
5608                     if(ci.checked == true){
5609                         r[++ri] = ci;
5610                     }
5611                 }
5612                 return r;
5613             },
5614
5615             "not" : function(c, ss){
5616                 return Roo.DomQuery.filter(c, ss, true);
5617             },
5618
5619             "odd" : function(c){
5620                 return this["nth-child"](c, "odd");
5621             },
5622
5623             "even" : function(c){
5624                 return this["nth-child"](c, "even");
5625             },
5626
5627             "nth" : function(c, a){
5628                 return c[a-1] || [];
5629             },
5630
5631             "first" : function(c){
5632                 return c[0] || [];
5633             },
5634
5635             "last" : function(c){
5636                 return c[c.length-1] || [];
5637             },
5638
5639             "has" : function(c, ss){
5640                 var s = Roo.DomQuery.select;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     if(s(ss, ci).length > 0){
5644                         r[++ri] = ci;
5645                     }
5646                 }
5647                 return r;
5648             },
5649
5650             "next" : function(c, ss){
5651                 var is = Roo.DomQuery.is;
5652                 var r = [], ri = -1;
5653                 for(var i = 0, ci; ci = c[i]; i++){
5654                     var n = next(ci);
5655                     if(n && is(n, ss)){
5656                         r[++ri] = ci;
5657                     }
5658                 }
5659                 return r;
5660             },
5661
5662             "prev" : function(c, ss){
5663                 var is = Roo.DomQuery.is;
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     var n = prev(ci);
5667                     if(n && is(n, ss)){
5668                         r[++ri] = ci;
5669                     }
5670                 }
5671                 return r;
5672             }
5673         }
5674     };
5675 }();
5676
5677 /**
5678  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5679  * @param {String} path The selector/xpath query
5680  * @param {Node} root (optional) The start of the query (defaults to document).
5681  * @return {Array}
5682  * @member Roo
5683  * @method query
5684  */
5685 Roo.query = Roo.DomQuery.select;
5686 /*
5687  * Based on:
5688  * Ext JS Library 1.1.1
5689  * Copyright(c) 2006-2007, Ext JS, LLC.
5690  *
5691  * Originally Released Under LGPL - original licence link has changed is not relivant.
5692  *
5693  * Fork - LGPL
5694  * <script type="text/javascript">
5695  */
5696
5697 /**
5698  * @class Roo.util.Observable
5699  * Base class that provides a common interface for publishing events. Subclasses are expected to
5700  * to have a property "events" with all the events defined.<br>
5701  * For example:
5702  * <pre><code>
5703  Employee = function(name){
5704     this.name = name;
5705     this.addEvents({
5706         "fired" : true,
5707         "quit" : true
5708     });
5709  }
5710  Roo.extend(Employee, Roo.util.Observable);
5711 </code></pre>
5712  * @param {Object} config properties to use (incuding events / listeners)
5713  */
5714
5715 Roo.util.Observable = function(cfg){
5716     
5717     cfg = cfg|| {};
5718     this.addEvents(cfg.events || {});
5719     if (cfg.events) {
5720         delete cfg.events; // make sure
5721     }
5722      
5723     Roo.apply(this, cfg);
5724     
5725     if(this.listeners){
5726         this.on(this.listeners);
5727         delete this.listeners;
5728     }
5729 };
5730 Roo.util.Observable.prototype = {
5731     /** 
5732  * @cfg {Object} listeners  list of events and functions to call for this object, 
5733  * For example :
5734  * <pre><code>
5735     listeners :  { 
5736        'click' : function(e) {
5737            ..... 
5738         } ,
5739         .... 
5740     } 
5741   </code></pre>
5742  */
5743     
5744     
5745     /**
5746      * Fires the specified event with the passed parameters (minus the event name).
5747      * @param {String} eventName
5748      * @param {Object...} args Variable number of parameters are passed to handlers
5749      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5750      */
5751     fireEvent : function(){
5752         var ce = this.events[arguments[0].toLowerCase()];
5753         if(typeof ce == "object"){
5754             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5755         }else{
5756             return true;
5757         }
5758     },
5759
5760     // private
5761     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5762
5763     /**
5764      * Appends an event handler to this component
5765      * @param {String}   eventName The type of event to listen for
5766      * @param {Function} handler The method the event invokes
5767      * @param {Object}   scope (optional) The scope in which to execute the handler
5768      * function. The handler function's "this" context.
5769      * @param {Object}   options (optional) An object containing handler configuration
5770      * properties. This may contain any of the following properties:<ul>
5771      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5772      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5773      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5774      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5775      * by the specified number of milliseconds. If the event fires again within that time, the original
5776      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5777      * </ul><br>
5778      * <p>
5779      * <b>Combining Options</b><br>
5780      * Using the options argument, it is possible to combine different types of listeners:<br>
5781      * <br>
5782      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5783                 <pre><code>
5784                 el.on('click', this.onClick, this, {
5785                         single: true,
5786                 delay: 100,
5787                 forumId: 4
5788                 });
5789                 </code></pre>
5790      * <p>
5791      * <b>Attaching multiple handlers in 1 call</b><br>
5792      * The method also allows for a single argument to be passed which is a config object containing properties
5793      * which specify multiple handlers.
5794      * <pre><code>
5795                 el.on({
5796                         'click': {
5797                         fn: this.onClick,
5798                         scope: this,
5799                         delay: 100
5800                 }, 
5801                 'mouseover': {
5802                         fn: this.onMouseOver,
5803                         scope: this
5804                 },
5805                 'mouseout': {
5806                         fn: this.onMouseOut,
5807                         scope: this
5808                 }
5809                 });
5810                 </code></pre>
5811      * <p>
5812      * Or a shorthand syntax which passes the same scope object to all handlers:
5813         <pre><code>
5814                 el.on({
5815                         'click': this.onClick,
5816                 'mouseover': this.onMouseOver,
5817                 'mouseout': this.onMouseOut,
5818                 scope: this
5819                 });
5820                 </code></pre>
5821      */
5822     addListener : function(eventName, fn, scope, o){
5823         if(typeof eventName == "object"){
5824             o = eventName;
5825             for(var e in o){
5826                 if(this.filterOptRe.test(e)){
5827                     continue;
5828                 }
5829                 if(typeof o[e] == "function"){
5830                     // shared options
5831                     this.addListener(e, o[e], o.scope,  o);
5832                 }else{
5833                     // individual options
5834                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5835                 }
5836             }
5837             return;
5838         }
5839         o = (!o || typeof o == "boolean") ? {} : o;
5840         eventName = eventName.toLowerCase();
5841         var ce = this.events[eventName] || true;
5842         if(typeof ce == "boolean"){
5843             ce = new Roo.util.Event(this, eventName);
5844             this.events[eventName] = ce;
5845         }
5846         ce.addListener(fn, scope, o);
5847     },
5848
5849     /**
5850      * Removes a listener
5851      * @param {String}   eventName     The type of event to listen for
5852      * @param {Function} handler        The handler to remove
5853      * @param {Object}   scope  (optional) The scope (this object) for the handler
5854      */
5855     removeListener : function(eventName, fn, scope){
5856         var ce = this.events[eventName.toLowerCase()];
5857         if(typeof ce == "object"){
5858             ce.removeListener(fn, scope);
5859         }
5860     },
5861
5862     /**
5863      * Removes all listeners for this object
5864      */
5865     purgeListeners : function(){
5866         for(var evt in this.events){
5867             if(typeof this.events[evt] == "object"){
5868                  this.events[evt].clearListeners();
5869             }
5870         }
5871     },
5872
5873     relayEvents : function(o, events){
5874         var createHandler = function(ename){
5875             return function(){
5876                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5877             };
5878         };
5879         for(var i = 0, len = events.length; i < len; i++){
5880             var ename = events[i];
5881             if(!this.events[ename]){ this.events[ename] = true; };
5882             o.on(ename, createHandler(ename), this);
5883         }
5884     },
5885
5886     /**
5887      * Used to define events on this Observable
5888      * @param {Object} object The object with the events defined
5889      */
5890     addEvents : function(o){
5891         if(!this.events){
5892             this.events = {};
5893         }
5894         Roo.applyIf(this.events, o);
5895     },
5896
5897     /**
5898      * Checks to see if this object has any listeners for a specified event
5899      * @param {String} eventName The name of the event to check for
5900      * @return {Boolean} True if the event is being listened for, else false
5901      */
5902     hasListener : function(eventName){
5903         var e = this.events[eventName];
5904         return typeof e == "object" && e.listeners.length > 0;
5905     }
5906 };
5907 /**
5908  * Appends an event handler to this element (shorthand for addListener)
5909  * @param {String}   eventName     The type of event to listen for
5910  * @param {Function} handler        The method the event invokes
5911  * @param {Object}   scope (optional) The scope in which to execute the handler
5912  * function. The handler function's "this" context.
5913  * @param {Object}   options  (optional)
5914  * @method
5915  */
5916 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5917 /**
5918  * Removes a listener (shorthand for removeListener)
5919  * @param {String}   eventName     The type of event to listen for
5920  * @param {Function} handler        The handler to remove
5921  * @param {Object}   scope  (optional) The scope (this object) for the handler
5922  * @method
5923  */
5924 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5925
5926 /**
5927  * Starts capture on the specified Observable. All events will be passed
5928  * to the supplied function with the event name + standard signature of the event
5929  * <b>before</b> the event is fired. If the supplied function returns false,
5930  * the event will not fire.
5931  * @param {Observable} o The Observable to capture
5932  * @param {Function} fn The function to call
5933  * @param {Object} scope (optional) The scope (this object) for the fn
5934  * @static
5935  */
5936 Roo.util.Observable.capture = function(o, fn, scope){
5937     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5938 };
5939
5940 /**
5941  * Removes <b>all</b> added captures from the Observable.
5942  * @param {Observable} o The Observable to release
5943  * @static
5944  */
5945 Roo.util.Observable.releaseCapture = function(o){
5946     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5947 };
5948
5949 (function(){
5950
5951     var createBuffered = function(h, o, scope){
5952         var task = new Roo.util.DelayedTask();
5953         return function(){
5954             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5955         };
5956     };
5957
5958     var createSingle = function(h, e, fn, scope){
5959         return function(){
5960             e.removeListener(fn, scope);
5961             return h.apply(scope, arguments);
5962         };
5963     };
5964
5965     var createDelayed = function(h, o, scope){
5966         return function(){
5967             var args = Array.prototype.slice.call(arguments, 0);
5968             setTimeout(function(){
5969                 h.apply(scope, args);
5970             }, o.delay || 10);
5971         };
5972     };
5973
5974     Roo.util.Event = function(obj, name){
5975         this.name = name;
5976         this.obj = obj;
5977         this.listeners = [];
5978     };
5979
5980     Roo.util.Event.prototype = {
5981         addListener : function(fn, scope, options){
5982             var o = options || {};
5983             scope = scope || this.obj;
5984             if(!this.isListening(fn, scope)){
5985                 var l = {fn: fn, scope: scope, options: o};
5986                 var h = fn;
5987                 if(o.delay){
5988                     h = createDelayed(h, o, scope);
5989                 }
5990                 if(o.single){
5991                     h = createSingle(h, this, fn, scope);
5992                 }
5993                 if(o.buffer){
5994                     h = createBuffered(h, o, scope);
5995                 }
5996                 l.fireFn = h;
5997                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5998                     this.listeners.push(l);
5999                 }else{
6000                     this.listeners = this.listeners.slice(0);
6001                     this.listeners.push(l);
6002                 }
6003             }
6004         },
6005
6006         findListener : function(fn, scope){
6007             scope = scope || this.obj;
6008             var ls = this.listeners;
6009             for(var i = 0, len = ls.length; i < len; i++){
6010                 var l = ls[i];
6011                 if(l.fn == fn && l.scope == scope){
6012                     return i;
6013                 }
6014             }
6015             return -1;
6016         },
6017
6018         isListening : function(fn, scope){
6019             return this.findListener(fn, scope) != -1;
6020         },
6021
6022         removeListener : function(fn, scope){
6023             var index;
6024             if((index = this.findListener(fn, scope)) != -1){
6025                 if(!this.firing){
6026                     this.listeners.splice(index, 1);
6027                 }else{
6028                     this.listeners = this.listeners.slice(0);
6029                     this.listeners.splice(index, 1);
6030                 }
6031                 return true;
6032             }
6033             return false;
6034         },
6035
6036         clearListeners : function(){
6037             this.listeners = [];
6038         },
6039
6040         fire : function(){
6041             var ls = this.listeners, scope, len = ls.length;
6042             if(len > 0){
6043                 this.firing = true;
6044                 var args = Array.prototype.slice.call(arguments, 0);
6045                 for(var i = 0; i < len; i++){
6046                     var l = ls[i];
6047                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6048                         this.firing = false;
6049                         return false;
6050                     }
6051                 }
6052                 this.firing = false;
6053             }
6054             return true;
6055         }
6056     };
6057 })();/*
6058  * RooJS Library 
6059  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6060  *
6061  * Licence LGPL 
6062  *
6063  */
6064  
6065 /**
6066  * @class Roo.Document
6067  * @extends Roo.util.Observable
6068  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6069  * 
6070  * @param {Object} config the methods and properties of the 'base' class for the application.
6071  * 
6072  *  Generic Page handler - implement this to start your app..
6073  * 
6074  * eg.
6075  *  MyProject = new Roo.Document({
6076         events : {
6077             'load' : true // your events..
6078         },
6079         listeners : {
6080             'ready' : function() {
6081                 // fired on Roo.onReady()
6082             }
6083         }
6084  * 
6085  */
6086 Roo.Document = function(cfg) {
6087      
6088     this.addEvents({ 
6089         'ready' : true
6090     });
6091     Roo.util.Observable.call(this,cfg);
6092     
6093     var _this = this;
6094     
6095     Roo.onReady(function() {
6096         _this.fireEvent('ready');
6097     },null,false);
6098     
6099     
6100 }
6101
6102 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6103  * Based on:
6104  * Ext JS Library 1.1.1
6105  * Copyright(c) 2006-2007, Ext JS, LLC.
6106  *
6107  * Originally Released Under LGPL - original licence link has changed is not relivant.
6108  *
6109  * Fork - LGPL
6110  * <script type="text/javascript">
6111  */
6112
6113 /**
6114  * @class Roo.EventManager
6115  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6116  * several useful events directly.
6117  * See {@link Roo.EventObject} for more details on normalized event objects.
6118  * @singleton
6119  */
6120 Roo.EventManager = function(){
6121     var docReadyEvent, docReadyProcId, docReadyState = false;
6122     var resizeEvent, resizeTask, textEvent, textSize;
6123     var E = Roo.lib.Event;
6124     var D = Roo.lib.Dom;
6125
6126     
6127     
6128
6129     var fireDocReady = function(){
6130         if(!docReadyState){
6131             docReadyState = true;
6132             Roo.isReady = true;
6133             if(docReadyProcId){
6134                 clearInterval(docReadyProcId);
6135             }
6136             if(Roo.isGecko || Roo.isOpera) {
6137                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6138             }
6139             if(Roo.isIE){
6140                 var defer = document.getElementById("ie-deferred-loader");
6141                 if(defer){
6142                     defer.onreadystatechange = null;
6143                     defer.parentNode.removeChild(defer);
6144                 }
6145             }
6146             if(docReadyEvent){
6147                 docReadyEvent.fire();
6148                 docReadyEvent.clearListeners();
6149             }
6150         }
6151     };
6152     
6153     var initDocReady = function(){
6154         docReadyEvent = new Roo.util.Event();
6155         if(Roo.isGecko || Roo.isOpera) {
6156             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6157         }else if(Roo.isIE){
6158             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6159             var defer = document.getElementById("ie-deferred-loader");
6160             defer.onreadystatechange = function(){
6161                 if(this.readyState == "complete"){
6162                     fireDocReady();
6163                 }
6164             };
6165         }else if(Roo.isSafari){ 
6166             docReadyProcId = setInterval(function(){
6167                 var rs = document.readyState;
6168                 if(rs == "complete") {
6169                     fireDocReady();     
6170                  }
6171             }, 10);
6172         }
6173         // no matter what, make sure it fires on load
6174         E.on(window, "load", fireDocReady);
6175     };
6176
6177     var createBuffered = function(h, o){
6178         var task = new Roo.util.DelayedTask(h);
6179         return function(e){
6180             // create new event object impl so new events don't wipe out properties
6181             e = new Roo.EventObjectImpl(e);
6182             task.delay(o.buffer, h, null, [e]);
6183         };
6184     };
6185
6186     var createSingle = function(h, el, ename, fn){
6187         return function(e){
6188             Roo.EventManager.removeListener(el, ename, fn);
6189             h(e);
6190         };
6191     };
6192
6193     var createDelayed = function(h, o){
6194         return function(e){
6195             // create new event object impl so new events don't wipe out properties
6196             e = new Roo.EventObjectImpl(e);
6197             setTimeout(function(){
6198                 h(e);
6199             }, o.delay || 10);
6200         };
6201     };
6202     var transitionEndVal = false;
6203     
6204     var transitionEnd = function()
6205     {
6206         if (transitionEndVal) {
6207             return transitionEndVal;
6208         }
6209         var el = document.createElement('div');
6210
6211         var transEndEventNames = {
6212             WebkitTransition : 'webkitTransitionEnd',
6213             MozTransition    : 'transitionend',
6214             OTransition      : 'oTransitionEnd otransitionend',
6215             transition       : 'transitionend'
6216         };
6217     
6218         for (var name in transEndEventNames) {
6219             if (el.style[name] !== undefined) {
6220                 transitionEndVal = transEndEventNames[name];
6221                 return  transitionEndVal ;
6222             }
6223         }
6224     }
6225     
6226
6227     var listen = function(element, ename, opt, fn, scope){
6228         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6229         fn = fn || o.fn; scope = scope || o.scope;
6230         var el = Roo.getDom(element);
6231         
6232         
6233         if(!el){
6234             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6235         }
6236         
6237         if (ename == 'transitionend') {
6238             ename = transitionEnd();
6239         }
6240         var h = function(e){
6241             e = Roo.EventObject.setEvent(e);
6242             var t;
6243             if(o.delegate){
6244                 t = e.getTarget(o.delegate, el);
6245                 if(!t){
6246                     return;
6247                 }
6248             }else{
6249                 t = e.target;
6250             }
6251             if(o.stopEvent === true){
6252                 e.stopEvent();
6253             }
6254             if(o.preventDefault === true){
6255                e.preventDefault();
6256             }
6257             if(o.stopPropagation === true){
6258                 e.stopPropagation();
6259             }
6260
6261             if(o.normalized === false){
6262                 e = e.browserEvent;
6263             }
6264
6265             fn.call(scope || el, e, t, o);
6266         };
6267         if(o.delay){
6268             h = createDelayed(h, o);
6269         }
6270         if(o.single){
6271             h = createSingle(h, el, ename, fn);
6272         }
6273         if(o.buffer){
6274             h = createBuffered(h, o);
6275         }
6276         fn._handlers = fn._handlers || [];
6277         
6278         
6279         fn._handlers.push([Roo.id(el), ename, h]);
6280         
6281         
6282          
6283         E.on(el, ename, h);
6284         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6285             el.addEventListener("DOMMouseScroll", h, false);
6286             E.on(window, 'unload', function(){
6287                 el.removeEventListener("DOMMouseScroll", h, false);
6288             });
6289         }
6290         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6291             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6292         }
6293         return h;
6294     };
6295
6296     var stopListening = function(el, ename, fn){
6297         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6298         if(hds){
6299             for(var i = 0, len = hds.length; i < len; i++){
6300                 var h = hds[i];
6301                 if(h[0] == id && h[1] == ename){
6302                     hd = h[2];
6303                     hds.splice(i, 1);
6304                     break;
6305                 }
6306             }
6307         }
6308         E.un(el, ename, hd);
6309         el = Roo.getDom(el);
6310         if(ename == "mousewheel" && el.addEventListener){
6311             el.removeEventListener("DOMMouseScroll", hd, false);
6312         }
6313         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6314             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6315         }
6316     };
6317
6318     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6319     
6320     var pub = {
6321         
6322         
6323         /** 
6324          * Fix for doc tools
6325          * @scope Roo.EventManager
6326          */
6327         
6328         
6329         /** 
6330          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6331          * object with a Roo.EventObject
6332          * @param {Function} fn        The method the event invokes
6333          * @param {Object}   scope    An object that becomes the scope of the handler
6334          * @param {boolean}  override If true, the obj passed in becomes
6335          *                             the execution scope of the listener
6336          * @return {Function} The wrapped function
6337          * @deprecated
6338          */
6339         wrap : function(fn, scope, override){
6340             return function(e){
6341                 Roo.EventObject.setEvent(e);
6342                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6343             };
6344         },
6345         
6346         /**
6347      * Appends an event handler to an element (shorthand for addListener)
6348      * @param {String/HTMLElement}   element        The html element or id to assign the
6349      * @param {String}   eventName The type of event to listen for
6350      * @param {Function} handler The method the event invokes
6351      * @param {Object}   scope (optional) The scope in which to execute the handler
6352      * function. The handler function's "this" context.
6353      * @param {Object}   options (optional) An object containing handler configuration
6354      * properties. This may contain any of the following properties:<ul>
6355      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6356      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6357      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6358      * <li>preventDefault {Boolean} True to prevent the default action</li>
6359      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6360      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6361      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6362      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6363      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6364      * by the specified number of milliseconds. If the event fires again within that time, the original
6365      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6366      * </ul><br>
6367      * <p>
6368      * <b>Combining Options</b><br>
6369      * Using the options argument, it is possible to combine different types of listeners:<br>
6370      * <br>
6371      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6372      * Code:<pre><code>
6373 el.on('click', this.onClick, this, {
6374     single: true,
6375     delay: 100,
6376     stopEvent : true,
6377     forumId: 4
6378 });</code></pre>
6379      * <p>
6380      * <b>Attaching multiple handlers in 1 call</b><br>
6381       * The method also allows for a single argument to be passed which is a config object containing properties
6382      * which specify multiple handlers.
6383      * <p>
6384      * Code:<pre><code>
6385 el.on({
6386     'click' : {
6387         fn: this.onClick
6388         scope: this,
6389         delay: 100
6390     },
6391     'mouseover' : {
6392         fn: this.onMouseOver
6393         scope: this
6394     },
6395     'mouseout' : {
6396         fn: this.onMouseOut
6397         scope: this
6398     }
6399 });</code></pre>
6400      * <p>
6401      * Or a shorthand syntax:<br>
6402      * Code:<pre><code>
6403 el.on({
6404     'click' : this.onClick,
6405     'mouseover' : this.onMouseOver,
6406     'mouseout' : this.onMouseOut
6407     scope: this
6408 });</code></pre>
6409      */
6410         addListener : function(element, eventName, fn, scope, options){
6411             if(typeof eventName == "object"){
6412                 var o = eventName;
6413                 for(var e in o){
6414                     if(propRe.test(e)){
6415                         continue;
6416                     }
6417                     if(typeof o[e] == "function"){
6418                         // shared options
6419                         listen(element, e, o, o[e], o.scope);
6420                     }else{
6421                         // individual options
6422                         listen(element, e, o[e]);
6423                     }
6424                 }
6425                 return;
6426             }
6427             return listen(element, eventName, options, fn, scope);
6428         },
6429         
6430         /**
6431          * Removes an event handler
6432          *
6433          * @param {String/HTMLElement}   element        The id or html element to remove the 
6434          *                             event from
6435          * @param {String}   eventName     The type of event
6436          * @param {Function} fn
6437          * @return {Boolean} True if a listener was actually removed
6438          */
6439         removeListener : function(element, eventName, fn){
6440             return stopListening(element, eventName, fn);
6441         },
6442         
6443         /**
6444          * Fires when the document is ready (before onload and before images are loaded). Can be 
6445          * accessed shorthanded Roo.onReady().
6446          * @param {Function} fn        The method the event invokes
6447          * @param {Object}   scope    An  object that becomes the scope of the handler
6448          * @param {boolean}  options
6449          */
6450         onDocumentReady : function(fn, scope, options){
6451             if(docReadyState){ // if it already fired
6452                 docReadyEvent.addListener(fn, scope, options);
6453                 docReadyEvent.fire();
6454                 docReadyEvent.clearListeners();
6455                 return;
6456             }
6457             if(!docReadyEvent){
6458                 initDocReady();
6459             }
6460             docReadyEvent.addListener(fn, scope, options);
6461         },
6462         
6463         /**
6464          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6465          * @param {Function} fn        The method the event invokes
6466          * @param {Object}   scope    An object that becomes the scope of the handler
6467          * @param {boolean}  options
6468          */
6469         onWindowResize : function(fn, scope, options){
6470             if(!resizeEvent){
6471                 resizeEvent = new Roo.util.Event();
6472                 resizeTask = new Roo.util.DelayedTask(function(){
6473                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6474                 });
6475                 E.on(window, "resize", function(){
6476                     if(Roo.isIE){
6477                         resizeTask.delay(50);
6478                     }else{
6479                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6480                     }
6481                 });
6482             }
6483             resizeEvent.addListener(fn, scope, options);
6484         },
6485
6486         /**
6487          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6488          * @param {Function} fn        The method the event invokes
6489          * @param {Object}   scope    An object that becomes the scope of the handler
6490          * @param {boolean}  options
6491          */
6492         onTextResize : function(fn, scope, options){
6493             if(!textEvent){
6494                 textEvent = new Roo.util.Event();
6495                 var textEl = new Roo.Element(document.createElement('div'));
6496                 textEl.dom.className = 'x-text-resize';
6497                 textEl.dom.innerHTML = 'X';
6498                 textEl.appendTo(document.body);
6499                 textSize = textEl.dom.offsetHeight;
6500                 setInterval(function(){
6501                     if(textEl.dom.offsetHeight != textSize){
6502                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6503                     }
6504                 }, this.textResizeInterval);
6505             }
6506             textEvent.addListener(fn, scope, options);
6507         },
6508
6509         /**
6510          * Removes the passed window resize listener.
6511          * @param {Function} fn        The method the event invokes
6512          * @param {Object}   scope    The scope of handler
6513          */
6514         removeResizeListener : function(fn, scope){
6515             if(resizeEvent){
6516                 resizeEvent.removeListener(fn, scope);
6517             }
6518         },
6519
6520         // private
6521         fireResize : function(){
6522             if(resizeEvent){
6523                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6524             }   
6525         },
6526         /**
6527          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6528          */
6529         ieDeferSrc : false,
6530         /**
6531          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6532          */
6533         textResizeInterval : 50
6534     };
6535     
6536     /**
6537      * Fix for doc tools
6538      * @scopeAlias pub=Roo.EventManager
6539      */
6540     
6541      /**
6542      * Appends an event handler to an element (shorthand for addListener)
6543      * @param {String/HTMLElement}   element        The html element or id to assign the
6544      * @param {String}   eventName The type of event to listen for
6545      * @param {Function} handler The method the event invokes
6546      * @param {Object}   scope (optional) The scope in which to execute the handler
6547      * function. The handler function's "this" context.
6548      * @param {Object}   options (optional) An object containing handler configuration
6549      * properties. This may contain any of the following properties:<ul>
6550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6551      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6552      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6553      * <li>preventDefault {Boolean} True to prevent the default action</li>
6554      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6555      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6556      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6557      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6558      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6559      * by the specified number of milliseconds. If the event fires again within that time, the original
6560      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6561      * </ul><br>
6562      * <p>
6563      * <b>Combining Options</b><br>
6564      * Using the options argument, it is possible to combine different types of listeners:<br>
6565      * <br>
6566      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6567      * Code:<pre><code>
6568 el.on('click', this.onClick, this, {
6569     single: true,
6570     delay: 100,
6571     stopEvent : true,
6572     forumId: 4
6573 });</code></pre>
6574      * <p>
6575      * <b>Attaching multiple handlers in 1 call</b><br>
6576       * The method also allows for a single argument to be passed which is a config object containing properties
6577      * which specify multiple handlers.
6578      * <p>
6579      * Code:<pre><code>
6580 el.on({
6581     'click' : {
6582         fn: this.onClick
6583         scope: this,
6584         delay: 100
6585     },
6586     'mouseover' : {
6587         fn: this.onMouseOver
6588         scope: this
6589     },
6590     'mouseout' : {
6591         fn: this.onMouseOut
6592         scope: this
6593     }
6594 });</code></pre>
6595      * <p>
6596      * Or a shorthand syntax:<br>
6597      * Code:<pre><code>
6598 el.on({
6599     'click' : this.onClick,
6600     'mouseover' : this.onMouseOver,
6601     'mouseout' : this.onMouseOut
6602     scope: this
6603 });</code></pre>
6604      */
6605     pub.on = pub.addListener;
6606     pub.un = pub.removeListener;
6607
6608     pub.stoppedMouseDownEvent = new Roo.util.Event();
6609     return pub;
6610 }();
6611 /**
6612   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6613   * @param {Function} fn        The method the event invokes
6614   * @param {Object}   scope    An  object that becomes the scope of the handler
6615   * @param {boolean}  override If true, the obj passed in becomes
6616   *                             the execution scope of the listener
6617   * @member Roo
6618   * @method onReady
6619  */
6620 Roo.onReady = Roo.EventManager.onDocumentReady;
6621
6622 Roo.onReady(function(){
6623     var bd = Roo.get(document.body);
6624     if(!bd){ return; }
6625
6626     var cls = [
6627             Roo.isIE ? "roo-ie"
6628             : Roo.isGecko ? "roo-gecko"
6629             : Roo.isOpera ? "roo-opera"
6630             : Roo.isSafari ? "roo-safari" : ""];
6631
6632     if(Roo.isMac){
6633         cls.push("roo-mac");
6634     }
6635     if(Roo.isLinux){
6636         cls.push("roo-linux");
6637     }
6638     if(Roo.isIOS){
6639         cls.push("roo-ios");
6640     }
6641     if(Roo.isTouch){
6642         cls.push("roo-touch");
6643     }
6644     if(Roo.isBorderBox){
6645         cls.push('roo-border-box');
6646     }
6647     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6648         var p = bd.dom.parentNode;
6649         if(p){
6650             p.className += ' roo-strict';
6651         }
6652     }
6653     bd.addClass(cls.join(' '));
6654 });
6655
6656 /**
6657  * @class Roo.EventObject
6658  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6659  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6660  * Example:
6661  * <pre><code>
6662  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6663     e.preventDefault();
6664     var target = e.getTarget();
6665     ...
6666  }
6667  var myDiv = Roo.get("myDiv");
6668  myDiv.on("click", handleClick);
6669  //or
6670  Roo.EventManager.on("myDiv", 'click', handleClick);
6671  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6672  </code></pre>
6673  * @singleton
6674  */
6675 Roo.EventObject = function(){
6676     
6677     var E = Roo.lib.Event;
6678     
6679     // safari keypress events for special keys return bad keycodes
6680     var safariKeys = {
6681         63234 : 37, // left
6682         63235 : 39, // right
6683         63232 : 38, // up
6684         63233 : 40, // down
6685         63276 : 33, // page up
6686         63277 : 34, // page down
6687         63272 : 46, // delete
6688         63273 : 36, // home
6689         63275 : 35  // end
6690     };
6691
6692     // normalize button clicks
6693     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6694                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6695
6696     Roo.EventObjectImpl = function(e){
6697         if(e){
6698             this.setEvent(e.browserEvent || e);
6699         }
6700     };
6701     Roo.EventObjectImpl.prototype = {
6702         /**
6703          * Used to fix doc tools.
6704          * @scope Roo.EventObject.prototype
6705          */
6706             
6707
6708         
6709         
6710         /** The normal browser event */
6711         browserEvent : null,
6712         /** The button pressed in a mouse event */
6713         button : -1,
6714         /** True if the shift key was down during the event */
6715         shiftKey : false,
6716         /** True if the control key was down during the event */
6717         ctrlKey : false,
6718         /** True if the alt key was down during the event */
6719         altKey : false,
6720
6721         /** Key constant 
6722         * @type Number */
6723         BACKSPACE : 8,
6724         /** Key constant 
6725         * @type Number */
6726         TAB : 9,
6727         /** Key constant 
6728         * @type Number */
6729         RETURN : 13,
6730         /** Key constant 
6731         * @type Number */
6732         ENTER : 13,
6733         /** Key constant 
6734         * @type Number */
6735         SHIFT : 16,
6736         /** Key constant 
6737         * @type Number */
6738         CONTROL : 17,
6739         /** Key constant 
6740         * @type Number */
6741         ESC : 27,
6742         /** Key constant 
6743         * @type Number */
6744         SPACE : 32,
6745         /** Key constant 
6746         * @type Number */
6747         PAGEUP : 33,
6748         /** Key constant 
6749         * @type Number */
6750         PAGEDOWN : 34,
6751         /** Key constant 
6752         * @type Number */
6753         END : 35,
6754         /** Key constant 
6755         * @type Number */
6756         HOME : 36,
6757         /** Key constant 
6758         * @type Number */
6759         LEFT : 37,
6760         /** Key constant 
6761         * @type Number */
6762         UP : 38,
6763         /** Key constant 
6764         * @type Number */
6765         RIGHT : 39,
6766         /** Key constant 
6767         * @type Number */
6768         DOWN : 40,
6769         /** Key constant 
6770         * @type Number */
6771         DELETE : 46,
6772         /** Key constant 
6773         * @type Number */
6774         F5 : 116,
6775
6776            /** @private */
6777         setEvent : function(e){
6778             if(e == this || (e && e.browserEvent)){ // already wrapped
6779                 return e;
6780             }
6781             this.browserEvent = e;
6782             if(e){
6783                 // normalize buttons
6784                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6785                 if(e.type == 'click' && this.button == -1){
6786                     this.button = 0;
6787                 }
6788                 this.type = e.type;
6789                 this.shiftKey = e.shiftKey;
6790                 // mac metaKey behaves like ctrlKey
6791                 this.ctrlKey = e.ctrlKey || e.metaKey;
6792                 this.altKey = e.altKey;
6793                 // in getKey these will be normalized for the mac
6794                 this.keyCode = e.keyCode;
6795                 // keyup warnings on firefox.
6796                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6797                 // cache the target for the delayed and or buffered events
6798                 this.target = E.getTarget(e);
6799                 // same for XY
6800                 this.xy = E.getXY(e);
6801             }else{
6802                 this.button = -1;
6803                 this.shiftKey = false;
6804                 this.ctrlKey = false;
6805                 this.altKey = false;
6806                 this.keyCode = 0;
6807                 this.charCode =0;
6808                 this.target = null;
6809                 this.xy = [0, 0];
6810             }
6811             return this;
6812         },
6813
6814         /**
6815          * Stop the event (preventDefault and stopPropagation)
6816          */
6817         stopEvent : function(){
6818             if(this.browserEvent){
6819                 if(this.browserEvent.type == 'mousedown'){
6820                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6821                 }
6822                 E.stopEvent(this.browserEvent);
6823             }
6824         },
6825
6826         /**
6827          * Prevents the browsers default handling of the event.
6828          */
6829         preventDefault : function(){
6830             if(this.browserEvent){
6831                 E.preventDefault(this.browserEvent);
6832             }
6833         },
6834
6835         /** @private */
6836         isNavKeyPress : function(){
6837             var k = this.keyCode;
6838             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6839             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6840         },
6841
6842         isSpecialKey : function(){
6843             var k = this.keyCode;
6844             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6845             (k == 16) || (k == 17) ||
6846             (k >= 18 && k <= 20) ||
6847             (k >= 33 && k <= 35) ||
6848             (k >= 36 && k <= 39) ||
6849             (k >= 44 && k <= 45);
6850         },
6851         /**
6852          * Cancels bubbling of the event.
6853          */
6854         stopPropagation : function(){
6855             if(this.browserEvent){
6856                 if(this.type == 'mousedown'){
6857                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6858                 }
6859                 E.stopPropagation(this.browserEvent);
6860             }
6861         },
6862
6863         /**
6864          * Gets the key code for the event.
6865          * @return {Number}
6866          */
6867         getCharCode : function(){
6868             return this.charCode || this.keyCode;
6869         },
6870
6871         /**
6872          * Returns a normalized keyCode for the event.
6873          * @return {Number} The key code
6874          */
6875         getKey : function(){
6876             var k = this.keyCode || this.charCode;
6877             return Roo.isSafari ? (safariKeys[k] || k) : k;
6878         },
6879
6880         /**
6881          * Gets the x coordinate of the event.
6882          * @return {Number}
6883          */
6884         getPageX : function(){
6885             return this.xy[0];
6886         },
6887
6888         /**
6889          * Gets the y coordinate of the event.
6890          * @return {Number}
6891          */
6892         getPageY : function(){
6893             return this.xy[1];
6894         },
6895
6896         /**
6897          * Gets the time of the event.
6898          * @return {Number}
6899          */
6900         getTime : function(){
6901             if(this.browserEvent){
6902                 return E.getTime(this.browserEvent);
6903             }
6904             return null;
6905         },
6906
6907         /**
6908          * Gets the page coordinates of the event.
6909          * @return {Array} The xy values like [x, y]
6910          */
6911         getXY : function(){
6912             return this.xy;
6913         },
6914
6915         /**
6916          * Gets the target for the event.
6917          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6918          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6919                 search as a number or element (defaults to 10 || document.body)
6920          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6921          * @return {HTMLelement}
6922          */
6923         getTarget : function(selector, maxDepth, returnEl){
6924             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6925         },
6926         /**
6927          * Gets the related target.
6928          * @return {HTMLElement}
6929          */
6930         getRelatedTarget : function(){
6931             if(this.browserEvent){
6932                 return E.getRelatedTarget(this.browserEvent);
6933             }
6934             return null;
6935         },
6936
6937         /**
6938          * Normalizes mouse wheel delta across browsers
6939          * @return {Number} The delta
6940          */
6941         getWheelDelta : function(){
6942             var e = this.browserEvent;
6943             var delta = 0;
6944             if(e.wheelDelta){ /* IE/Opera. */
6945                 delta = e.wheelDelta/120;
6946             }else if(e.detail){ /* Mozilla case. */
6947                 delta = -e.detail/3;
6948             }
6949             return delta;
6950         },
6951
6952         /**
6953          * Returns true if the control, meta, shift or alt key was pressed during this event.
6954          * @return {Boolean}
6955          */
6956         hasModifier : function(){
6957             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6958         },
6959
6960         /**
6961          * Returns true if the target of this event equals el or is a child of el
6962          * @param {String/HTMLElement/Element} el
6963          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6964          * @return {Boolean}
6965          */
6966         within : function(el, related){
6967             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6968             return t && Roo.fly(el).contains(t);
6969         },
6970
6971         getPoint : function(){
6972             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6973         }
6974     };
6975
6976     return new Roo.EventObjectImpl();
6977 }();
6978             
6979     /*
6980  * Based on:
6981  * Ext JS Library 1.1.1
6982  * Copyright(c) 2006-2007, Ext JS, LLC.
6983  *
6984  * Originally Released Under LGPL - original licence link has changed is not relivant.
6985  *
6986  * Fork - LGPL
6987  * <script type="text/javascript">
6988  */
6989
6990  
6991 // was in Composite Element!??!?!
6992  
6993 (function(){
6994     var D = Roo.lib.Dom;
6995     var E = Roo.lib.Event;
6996     var A = Roo.lib.Anim;
6997
6998     // local style camelizing for speed
6999     var propCache = {};
7000     var camelRe = /(-[a-z])/gi;
7001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7002     var view = document.defaultView;
7003
7004 /**
7005  * @class Roo.Element
7006  * Represents an Element in the DOM.<br><br>
7007  * Usage:<br>
7008 <pre><code>
7009 var el = Roo.get("my-div");
7010
7011 // or with getEl
7012 var el = getEl("my-div");
7013
7014 // or with a DOM element
7015 var el = Roo.get(myDivElement);
7016 </code></pre>
7017  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7018  * each call instead of constructing a new one.<br><br>
7019  * <b>Animations</b><br />
7020  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7021  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7022 <pre>
7023 Option    Default   Description
7024 --------- --------  ---------------------------------------------
7025 duration  .35       The duration of the animation in seconds
7026 easing    easeOut   The YUI easing method
7027 callback  none      A function to execute when the anim completes
7028 scope     this      The scope (this) of the callback function
7029 </pre>
7030 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7031 * manipulate the animation. Here's an example:
7032 <pre><code>
7033 var el = Roo.get("my-div");
7034
7035 // no animation
7036 el.setWidth(100);
7037
7038 // default animation
7039 el.setWidth(100, true);
7040
7041 // animation with some options set
7042 el.setWidth(100, {
7043     duration: 1,
7044     callback: this.foo,
7045     scope: this
7046 });
7047
7048 // using the "anim" property to get the Anim object
7049 var opt = {
7050     duration: 1,
7051     callback: this.foo,
7052     scope: this
7053 };
7054 el.setWidth(100, opt);
7055 ...
7056 if(opt.anim.isAnimated()){
7057     opt.anim.stop();
7058 }
7059 </code></pre>
7060 * <b> Composite (Collections of) Elements</b><br />
7061  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7062  * @constructor Create a new Element directly.
7063  * @param {String/HTMLElement} element
7064  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7065  */
7066     Roo.Element = function(element, forceNew){
7067         var dom = typeof element == "string" ?
7068                 document.getElementById(element) : element;
7069         if(!dom){ // invalid id/element
7070             return null;
7071         }
7072         var id = dom.id;
7073         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7074             return Roo.Element.cache[id];
7075         }
7076
7077         /**
7078          * The DOM element
7079          * @type HTMLElement
7080          */
7081         this.dom = dom;
7082
7083         /**
7084          * The DOM element ID
7085          * @type String
7086          */
7087         this.id = id || Roo.id(dom);
7088     };
7089
7090     var El = Roo.Element;
7091
7092     El.prototype = {
7093         /**
7094          * The element's default display mode  (defaults to "")
7095          * @type String
7096          */
7097         originalDisplay : "",
7098
7099         visibilityMode : 1,
7100         /**
7101          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7102          * @type String
7103          */
7104         defaultUnit : "px",
7105         
7106         /**
7107          * Sets the element's visibility mode. When setVisible() is called it
7108          * will use this to determine whether to set the visibility or the display property.
7109          * @param visMode Element.VISIBILITY or Element.DISPLAY
7110          * @return {Roo.Element} this
7111          */
7112         setVisibilityMode : function(visMode){
7113             this.visibilityMode = visMode;
7114             return this;
7115         },
7116         /**
7117          * Convenience method for setVisibilityMode(Element.DISPLAY)
7118          * @param {String} display (optional) What to set display to when visible
7119          * @return {Roo.Element} this
7120          */
7121         enableDisplayMode : function(display){
7122             this.setVisibilityMode(El.DISPLAY);
7123             if(typeof display != "undefined") { this.originalDisplay = display; }
7124             return this;
7125         },
7126
7127         /**
7128          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7129          * @param {String} selector The simple selector to test
7130          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7131                 search as a number or element (defaults to 10 || document.body)
7132          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7133          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7134          */
7135         findParent : function(simpleSelector, maxDepth, returnEl){
7136             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7137             maxDepth = maxDepth || 50;
7138             if(typeof maxDepth != "number"){
7139                 stopEl = Roo.getDom(maxDepth);
7140                 maxDepth = 10;
7141             }
7142             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7143                 if(dq.is(p, simpleSelector)){
7144                     return returnEl ? Roo.get(p) : p;
7145                 }
7146                 depth++;
7147                 p = p.parentNode;
7148             }
7149             return null;
7150         },
7151
7152
7153         /**
7154          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7155          * @param {String} selector The simple selector to test
7156          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7157                 search as a number or element (defaults to 10 || document.body)
7158          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7159          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7160          */
7161         findParentNode : function(simpleSelector, maxDepth, returnEl){
7162             var p = Roo.fly(this.dom.parentNode, '_internal');
7163             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7164         },
7165
7166         /**
7167          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7168          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7169          * @param {String} selector The simple selector to test
7170          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7171                 search as a number or element (defaults to 10 || document.body)
7172          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7173          */
7174         up : function(simpleSelector, maxDepth){
7175             return this.findParentNode(simpleSelector, maxDepth, true);
7176         },
7177
7178
7179
7180         /**
7181          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7182          * @param {String} selector The simple selector to test
7183          * @return {Boolean} True if this element matches the selector, else false
7184          */
7185         is : function(simpleSelector){
7186             return Roo.DomQuery.is(this.dom, simpleSelector);
7187         },
7188
7189         /**
7190          * Perform animation on this element.
7191          * @param {Object} args The YUI animation control args
7192          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7193          * @param {Function} onComplete (optional) Function to call when animation completes
7194          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7195          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7196          * @return {Roo.Element} this
7197          */
7198         animate : function(args, duration, onComplete, easing, animType){
7199             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7200             return this;
7201         },
7202
7203         /*
7204          * @private Internal animation call
7205          */
7206         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7207             animType = animType || 'run';
7208             opt = opt || {};
7209             var anim = Roo.lib.Anim[animType](
7210                 this.dom, args,
7211                 (opt.duration || defaultDur) || .35,
7212                 (opt.easing || defaultEase) || 'easeOut',
7213                 function(){
7214                     Roo.callback(cb, this);
7215                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7216                 },
7217                 this
7218             );
7219             opt.anim = anim;
7220             return anim;
7221         },
7222
7223         // private legacy anim prep
7224         preanim : function(a, i){
7225             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7226         },
7227
7228         /**
7229          * Removes worthless text nodes
7230          * @param {Boolean} forceReclean (optional) By default the element
7231          * keeps track if it has been cleaned already so
7232          * you can call this over and over. However, if you update the element and
7233          * need to force a reclean, you can pass true.
7234          */
7235         clean : function(forceReclean){
7236             if(this.isCleaned && forceReclean !== true){
7237                 return this;
7238             }
7239             var ns = /\S/;
7240             var d = this.dom, n = d.firstChild, ni = -1;
7241             while(n){
7242                 var nx = n.nextSibling;
7243                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7244                     d.removeChild(n);
7245                 }else{
7246                     n.nodeIndex = ++ni;
7247                 }
7248                 n = nx;
7249             }
7250             this.isCleaned = true;
7251             return this;
7252         },
7253
7254         // private
7255         calcOffsetsTo : function(el){
7256             el = Roo.get(el);
7257             var d = el.dom;
7258             var restorePos = false;
7259             if(el.getStyle('position') == 'static'){
7260                 el.position('relative');
7261                 restorePos = true;
7262             }
7263             var x = 0, y =0;
7264             var op = this.dom;
7265             while(op && op != d && op.tagName != 'HTML'){
7266                 x+= op.offsetLeft;
7267                 y+= op.offsetTop;
7268                 op = op.offsetParent;
7269             }
7270             if(restorePos){
7271                 el.position('static');
7272             }
7273             return [x, y];
7274         },
7275
7276         /**
7277          * Scrolls this element into view within the passed container.
7278          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7279          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7280          * @return {Roo.Element} this
7281          */
7282         scrollIntoView : function(container, hscroll){
7283             var c = Roo.getDom(container) || document.body;
7284             var el = this.dom;
7285
7286             var o = this.calcOffsetsTo(c),
7287                 l = o[0],
7288                 t = o[1],
7289                 b = t+el.offsetHeight,
7290                 r = l+el.offsetWidth;
7291
7292             var ch = c.clientHeight;
7293             var ct = parseInt(c.scrollTop, 10);
7294             var cl = parseInt(c.scrollLeft, 10);
7295             var cb = ct + ch;
7296             var cr = cl + c.clientWidth;
7297
7298             if(t < ct){
7299                 c.scrollTop = t;
7300             }else if(b > cb){
7301                 c.scrollTop = b-ch;
7302             }
7303
7304             if(hscroll !== false){
7305                 if(l < cl){
7306                     c.scrollLeft = l;
7307                 }else if(r > cr){
7308                     c.scrollLeft = r-c.clientWidth;
7309                 }
7310             }
7311             return this;
7312         },
7313
7314         // private
7315         scrollChildIntoView : function(child, hscroll){
7316             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7317         },
7318
7319         /**
7320          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7321          * the new height may not be available immediately.
7322          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7323          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7324          * @param {Function} onComplete (optional) Function to call when animation completes
7325          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7326          * @return {Roo.Element} this
7327          */
7328         autoHeight : function(animate, duration, onComplete, easing){
7329             var oldHeight = this.getHeight();
7330             this.clip();
7331             this.setHeight(1); // force clipping
7332             setTimeout(function(){
7333                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7334                 if(!animate){
7335                     this.setHeight(height);
7336                     this.unclip();
7337                     if(typeof onComplete == "function"){
7338                         onComplete();
7339                     }
7340                 }else{
7341                     this.setHeight(oldHeight); // restore original height
7342                     this.setHeight(height, animate, duration, function(){
7343                         this.unclip();
7344                         if(typeof onComplete == "function") { onComplete(); }
7345                     }.createDelegate(this), easing);
7346                 }
7347             }.createDelegate(this), 0);
7348             return this;
7349         },
7350
7351         /**
7352          * Returns true if this element is an ancestor of the passed element
7353          * @param {HTMLElement/String} el The element to check
7354          * @return {Boolean} True if this element is an ancestor of el, else false
7355          */
7356         contains : function(el){
7357             if(!el){return false;}
7358             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7359         },
7360
7361         /**
7362          * Checks whether the element is currently visible using both visibility and display properties.
7363          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7364          * @return {Boolean} True if the element is currently visible, else false
7365          */
7366         isVisible : function(deep) {
7367             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7368             if(deep !== true || !vis){
7369                 return vis;
7370             }
7371             var p = this.dom.parentNode;
7372             while(p && p.tagName.toLowerCase() != "body"){
7373                 if(!Roo.fly(p, '_isVisible').isVisible()){
7374                     return false;
7375                 }
7376                 p = p.parentNode;
7377             }
7378             return true;
7379         },
7380
7381         /**
7382          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7383          * @param {String} selector The CSS selector
7384          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7385          * @return {CompositeElement/CompositeElementLite} The composite element
7386          */
7387         select : function(selector, unique){
7388             return El.select(selector, unique, this.dom);
7389         },
7390
7391         /**
7392          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7393          * @param {String} selector The CSS selector
7394          * @return {Array} An array of the matched nodes
7395          */
7396         query : function(selector, unique){
7397             return Roo.DomQuery.select(selector, this.dom);
7398         },
7399
7400         /**
7401          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7402          * @param {String} selector The CSS selector
7403          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7404          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7405          */
7406         child : function(selector, returnDom){
7407             var n = Roo.DomQuery.selectNode(selector, this.dom);
7408             return returnDom ? n : Roo.get(n);
7409         },
7410
7411         /**
7412          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7413          * @param {String} selector The CSS selector
7414          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7415          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7416          */
7417         down : function(selector, returnDom){
7418             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7419             return returnDom ? n : Roo.get(n);
7420         },
7421
7422         /**
7423          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7424          * @param {String} group The group the DD object is member of
7425          * @param {Object} config The DD config object
7426          * @param {Object} overrides An object containing methods to override/implement on the DD object
7427          * @return {Roo.dd.DD} The DD object
7428          */
7429         initDD : function(group, config, overrides){
7430             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7431             return Roo.apply(dd, overrides);
7432         },
7433
7434         /**
7435          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7436          * @param {String} group The group the DDProxy object is member of
7437          * @param {Object} config The DDProxy config object
7438          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7439          * @return {Roo.dd.DDProxy} The DDProxy object
7440          */
7441         initDDProxy : function(group, config, overrides){
7442             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7443             return Roo.apply(dd, overrides);
7444         },
7445
7446         /**
7447          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7448          * @param {String} group The group the DDTarget object is member of
7449          * @param {Object} config The DDTarget config object
7450          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7451          * @return {Roo.dd.DDTarget} The DDTarget object
7452          */
7453         initDDTarget : function(group, config, overrides){
7454             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7455             return Roo.apply(dd, overrides);
7456         },
7457
7458         /**
7459          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7460          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7461          * @param {Boolean} visible Whether the element is visible
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          setVisible : function(visible, animate){
7466             if(!animate || !A){
7467                 if(this.visibilityMode == El.DISPLAY){
7468                     this.setDisplayed(visible);
7469                 }else{
7470                     this.fixDisplay();
7471                     this.dom.style.visibility = visible ? "visible" : "hidden";
7472                 }
7473             }else{
7474                 // closure for composites
7475                 var dom = this.dom;
7476                 var visMode = this.visibilityMode;
7477                 if(visible){
7478                     this.setOpacity(.01);
7479                     this.setVisible(true);
7480                 }
7481                 this.anim({opacity: { to: (visible?1:0) }},
7482                       this.preanim(arguments, 1),
7483                       null, .35, 'easeIn', function(){
7484                          if(!visible){
7485                              if(visMode == El.DISPLAY){
7486                                  dom.style.display = "none";
7487                              }else{
7488                                  dom.style.visibility = "hidden";
7489                              }
7490                              Roo.get(dom).setOpacity(1);
7491                          }
7492                      });
7493             }
7494             return this;
7495         },
7496
7497         /**
7498          * Returns true if display is not "none"
7499          * @return {Boolean}
7500          */
7501         isDisplayed : function() {
7502             return this.getStyle("display") != "none";
7503         },
7504
7505         /**
7506          * Toggles the element's visibility or display, depending on visibility mode.
7507          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7508          * @return {Roo.Element} this
7509          */
7510         toggle : function(animate){
7511             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7512             return this;
7513         },
7514
7515         /**
7516          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7517          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7518          * @return {Roo.Element} this
7519          */
7520         setDisplayed : function(value) {
7521             if(typeof value == "boolean"){
7522                value = value ? this.originalDisplay : "none";
7523             }
7524             this.setStyle("display", value);
7525             return this;
7526         },
7527
7528         /**
7529          * Tries to focus the element. Any exceptions are caught and ignored.
7530          * @return {Roo.Element} this
7531          */
7532         focus : function() {
7533             try{
7534                 this.dom.focus();
7535             }catch(e){}
7536             return this;
7537         },
7538
7539         /**
7540          * Tries to blur the element. Any exceptions are caught and ignored.
7541          * @return {Roo.Element} this
7542          */
7543         blur : function() {
7544             try{
7545                 this.dom.blur();
7546             }catch(e){}
7547             return this;
7548         },
7549
7550         /**
7551          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7552          * @param {String/Array} className The CSS class to add, or an array of classes
7553          * @return {Roo.Element} this
7554          */
7555         addClass : function(className){
7556             if(className instanceof Array){
7557                 for(var i = 0, len = className.length; i < len; i++) {
7558                     this.addClass(className[i]);
7559                 }
7560             }else{
7561                 if(className && !this.hasClass(className)){
7562                     this.dom.className = this.dom.className + " " + className;
7563                 }
7564             }
7565             return this;
7566         },
7567
7568         /**
7569          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7570          * @param {String/Array} className The CSS class to add, or an array of classes
7571          * @return {Roo.Element} this
7572          */
7573         radioClass : function(className){
7574             var siblings = this.dom.parentNode.childNodes;
7575             for(var i = 0; i < siblings.length; i++) {
7576                 var s = siblings[i];
7577                 if(s.nodeType == 1){
7578                     Roo.get(s).removeClass(className);
7579                 }
7580             }
7581             this.addClass(className);
7582             return this;
7583         },
7584
7585         /**
7586          * Removes one or more CSS classes from the element.
7587          * @param {String/Array} className The CSS class to remove, or an array of classes
7588          * @return {Roo.Element} this
7589          */
7590         removeClass : function(className){
7591             if(!className || !this.dom.className){
7592                 return this;
7593             }
7594             if(className instanceof Array){
7595                 for(var i = 0, len = className.length; i < len; i++) {
7596                     this.removeClass(className[i]);
7597                 }
7598             }else{
7599                 if(this.hasClass(className)){
7600                     var re = this.classReCache[className];
7601                     if (!re) {
7602                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7603                        this.classReCache[className] = re;
7604                     }
7605                     this.dom.className =
7606                         this.dom.className.replace(re, " ");
7607                 }
7608             }
7609             return this;
7610         },
7611
7612         // private
7613         classReCache: {},
7614
7615         /**
7616          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7617          * @param {String} className The CSS class to toggle
7618          * @return {Roo.Element} this
7619          */
7620         toggleClass : function(className){
7621             if(this.hasClass(className)){
7622                 this.removeClass(className);
7623             }else{
7624                 this.addClass(className);
7625             }
7626             return this;
7627         },
7628
7629         /**
7630          * Checks if the specified CSS class exists on this element's DOM node.
7631          * @param {String} className The CSS class to check for
7632          * @return {Boolean} True if the class exists, else false
7633          */
7634         hasClass : function(className){
7635             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7636         },
7637
7638         /**
7639          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7640          * @param {String} oldClassName The CSS class to replace
7641          * @param {String} newClassName The replacement CSS class
7642          * @return {Roo.Element} this
7643          */
7644         replaceClass : function(oldClassName, newClassName){
7645             this.removeClass(oldClassName);
7646             this.addClass(newClassName);
7647             return this;
7648         },
7649
7650         /**
7651          * Returns an object with properties matching the styles requested.
7652          * For example, el.getStyles('color', 'font-size', 'width') might return
7653          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7654          * @param {String} style1 A style name
7655          * @param {String} style2 A style name
7656          * @param {String} etc.
7657          * @return {Object} The style object
7658          */
7659         getStyles : function(){
7660             var a = arguments, len = a.length, r = {};
7661             for(var i = 0; i < len; i++){
7662                 r[a[i]] = this.getStyle(a[i]);
7663             }
7664             return r;
7665         },
7666
7667         /**
7668          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7669          * @param {String} property The style property whose value is returned.
7670          * @return {String} The current value of the style property for this element.
7671          */
7672         getStyle : function(){
7673             return view && view.getComputedStyle ?
7674                 function(prop){
7675                     var el = this.dom, v, cs, camel;
7676                     if(prop == 'float'){
7677                         prop = "cssFloat";
7678                     }
7679                     if(el.style && (v = el.style[prop])){
7680                         return v;
7681                     }
7682                     if(cs = view.getComputedStyle(el, "")){
7683                         if(!(camel = propCache[prop])){
7684                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7685                         }
7686                         return cs[camel];
7687                     }
7688                     return null;
7689                 } :
7690                 function(prop){
7691                     var el = this.dom, v, cs, camel;
7692                     if(prop == 'opacity'){
7693                         if(typeof el.style.filter == 'string'){
7694                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7695                             if(m){
7696                                 var fv = parseFloat(m[1]);
7697                                 if(!isNaN(fv)){
7698                                     return fv ? fv / 100 : 0;
7699                                 }
7700                             }
7701                         }
7702                         return 1;
7703                     }else if(prop == 'float'){
7704                         prop = "styleFloat";
7705                     }
7706                     if(!(camel = propCache[prop])){
7707                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7708                     }
7709                     if(v = el.style[camel]){
7710                         return v;
7711                     }
7712                     if(cs = el.currentStyle){
7713                         return cs[camel];
7714                     }
7715                     return null;
7716                 };
7717         }(),
7718
7719         /**
7720          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7721          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7722          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7723          * @return {Roo.Element} this
7724          */
7725         setStyle : function(prop, value){
7726             if(typeof prop == "string"){
7727                 
7728                 if (prop == 'float') {
7729                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7730                     return this;
7731                 }
7732                 
7733                 var camel;
7734                 if(!(camel = propCache[prop])){
7735                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7736                 }
7737                 
7738                 if(camel == 'opacity') {
7739                     this.setOpacity(value);
7740                 }else{
7741                     this.dom.style[camel] = value;
7742                 }
7743             }else{
7744                 for(var style in prop){
7745                     if(typeof prop[style] != "function"){
7746                        this.setStyle(style, prop[style]);
7747                     }
7748                 }
7749             }
7750             return this;
7751         },
7752
7753         /**
7754          * More flexible version of {@link #setStyle} for setting style properties.
7755          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7756          * a function which returns such a specification.
7757          * @return {Roo.Element} this
7758          */
7759         applyStyles : function(style){
7760             Roo.DomHelper.applyStyles(this.dom, style);
7761             return this;
7762         },
7763
7764         /**
7765           * 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).
7766           * @return {Number} The X position of the element
7767           */
7768         getX : function(){
7769             return D.getX(this.dom);
7770         },
7771
7772         /**
7773           * 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).
7774           * @return {Number} The Y position of the element
7775           */
7776         getY : function(){
7777             return D.getY(this.dom);
7778         },
7779
7780         /**
7781           * 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).
7782           * @return {Array} The XY position of the element
7783           */
7784         getXY : function(){
7785             return D.getXY(this.dom);
7786         },
7787
7788         /**
7789          * 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).
7790          * @param {Number} The X position of the element
7791          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794         setX : function(x, animate){
7795             if(!animate || !A){
7796                 D.setX(this.dom, x);
7797             }else{
7798                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7799             }
7800             return this;
7801         },
7802
7803         /**
7804          * 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).
7805          * @param {Number} The Y position of the element
7806          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7807          * @return {Roo.Element} this
7808          */
7809         setY : function(y, animate){
7810             if(!animate || !A){
7811                 D.setY(this.dom, y);
7812             }else{
7813                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7814             }
7815             return this;
7816         },
7817
7818         /**
7819          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7820          * @param {String} left The left CSS property value
7821          * @return {Roo.Element} this
7822          */
7823         setLeft : function(left){
7824             this.setStyle("left", this.addUnits(left));
7825             return this;
7826         },
7827
7828         /**
7829          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7830          * @param {String} top The top CSS property value
7831          * @return {Roo.Element} this
7832          */
7833         setTop : function(top){
7834             this.setStyle("top", this.addUnits(top));
7835             return this;
7836         },
7837
7838         /**
7839          * Sets the element's CSS right style.
7840          * @param {String} right The right CSS property value
7841          * @return {Roo.Element} this
7842          */
7843         setRight : function(right){
7844             this.setStyle("right", this.addUnits(right));
7845             return this;
7846         },
7847
7848         /**
7849          * Sets the element's CSS bottom style.
7850          * @param {String} bottom The bottom CSS property value
7851          * @return {Roo.Element} this
7852          */
7853         setBottom : function(bottom){
7854             this.setStyle("bottom", this.addUnits(bottom));
7855             return this;
7856         },
7857
7858         /**
7859          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7860          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7861          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7862          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7863          * @return {Roo.Element} this
7864          */
7865         setXY : function(pos, animate){
7866             if(!animate || !A){
7867                 D.setXY(this.dom, pos);
7868             }else{
7869                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7870             }
7871             return this;
7872         },
7873
7874         /**
7875          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7876          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7877          * @param {Number} x X value for new position (coordinates are page-based)
7878          * @param {Number} y Y value for new position (coordinates are page-based)
7879          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7880          * @return {Roo.Element} this
7881          */
7882         setLocation : function(x, y, animate){
7883             this.setXY([x, y], this.preanim(arguments, 2));
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7889          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7890          * @param {Number} x X value for new position (coordinates are page-based)
7891          * @param {Number} y Y value for new position (coordinates are page-based)
7892          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7893          * @return {Roo.Element} this
7894          */
7895         moveTo : function(x, y, animate){
7896             this.setXY([x, y], this.preanim(arguments, 2));
7897             return this;
7898         },
7899
7900         /**
7901          * Returns the region of the given element.
7902          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7903          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7904          */
7905         getRegion : function(){
7906             return D.getRegion(this.dom);
7907         },
7908
7909         /**
7910          * Returns the offset height of the element
7911          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7912          * @return {Number} The element's height
7913          */
7914         getHeight : function(contentHeight){
7915             var h = this.dom.offsetHeight || 0;
7916             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7917         },
7918
7919         /**
7920          * Returns the offset width of the element
7921          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7922          * @return {Number} The element's width
7923          */
7924         getWidth : function(contentWidth){
7925             var w = this.dom.offsetWidth || 0;
7926             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7927         },
7928
7929         /**
7930          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7931          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7932          * if a height has not been set using CSS.
7933          * @return {Number}
7934          */
7935         getComputedHeight : function(){
7936             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7937             if(!h){
7938                 h = parseInt(this.getStyle('height'), 10) || 0;
7939                 if(!this.isBorderBox()){
7940                     h += this.getFrameWidth('tb');
7941                 }
7942             }
7943             return h;
7944         },
7945
7946         /**
7947          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7948          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7949          * if a width has not been set using CSS.
7950          * @return {Number}
7951          */
7952         getComputedWidth : function(){
7953             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7954             if(!w){
7955                 w = parseInt(this.getStyle('width'), 10) || 0;
7956                 if(!this.isBorderBox()){
7957                     w += this.getFrameWidth('lr');
7958                 }
7959             }
7960             return w;
7961         },
7962
7963         /**
7964          * Returns the size of the element.
7965          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7966          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7967          */
7968         getSize : function(contentSize){
7969             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7970         },
7971
7972         /**
7973          * Returns the width and height of the viewport.
7974          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7975          */
7976         getViewSize : function(){
7977             var d = this.dom, doc = document, aw = 0, ah = 0;
7978             if(d == doc || d == doc.body){
7979                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7980             }else{
7981                 return {
7982                     width : d.clientWidth,
7983                     height: d.clientHeight
7984                 };
7985             }
7986         },
7987
7988         /**
7989          * Returns the value of the "value" attribute
7990          * @param {Boolean} asNumber true to parse the value as a number
7991          * @return {String/Number}
7992          */
7993         getValue : function(asNumber){
7994             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7995         },
7996
7997         // private
7998         adjustWidth : function(width){
7999             if(typeof width == "number"){
8000                 if(this.autoBoxAdjust && !this.isBorderBox()){
8001                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8002                 }
8003                 if(width < 0){
8004                     width = 0;
8005                 }
8006             }
8007             return width;
8008         },
8009
8010         // private
8011         adjustHeight : function(height){
8012             if(typeof height == "number"){
8013                if(this.autoBoxAdjust && !this.isBorderBox()){
8014                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8015                }
8016                if(height < 0){
8017                    height = 0;
8018                }
8019             }
8020             return height;
8021         },
8022
8023         /**
8024          * Set the width of the element
8025          * @param {Number} width The new width
8026          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setWidth : function(width, animate){
8030             width = this.adjustWidth(width);
8031             if(!animate || !A){
8032                 this.dom.style.width = this.addUnits(width);
8033             }else{
8034                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8035             }
8036             return this;
8037         },
8038
8039         /**
8040          * Set the height of the element
8041          * @param {Number} height The new height
8042          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8043          * @return {Roo.Element} this
8044          */
8045          setHeight : function(height, animate){
8046             height = this.adjustHeight(height);
8047             if(!animate || !A){
8048                 this.dom.style.height = this.addUnits(height);
8049             }else{
8050                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8051             }
8052             return this;
8053         },
8054
8055         /**
8056          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8057          * @param {Number} width The new width
8058          * @param {Number} height The new height
8059          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8060          * @return {Roo.Element} this
8061          */
8062          setSize : function(width, height, animate){
8063             if(typeof width == "object"){ // in case of object from getSize()
8064                 height = width.height; width = width.width;
8065             }
8066             width = this.adjustWidth(width); height = this.adjustHeight(height);
8067             if(!animate || !A){
8068                 this.dom.style.width = this.addUnits(width);
8069                 this.dom.style.height = this.addUnits(height);
8070             }else{
8071                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8072             }
8073             return this;
8074         },
8075
8076         /**
8077          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8078          * @param {Number} x X value for new position (coordinates are page-based)
8079          * @param {Number} y Y value for new position (coordinates are page-based)
8080          * @param {Number} width The new width
8081          * @param {Number} height The new height
8082          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8083          * @return {Roo.Element} this
8084          */
8085         setBounds : function(x, y, width, height, animate){
8086             if(!animate || !A){
8087                 this.setSize(width, height);
8088                 this.setLocation(x, y);
8089             }else{
8090                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8091                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8092                               this.preanim(arguments, 4), 'motion');
8093             }
8094             return this;
8095         },
8096
8097         /**
8098          * 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.
8099          * @param {Roo.lib.Region} region The region to fill
8100          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8101          * @return {Roo.Element} this
8102          */
8103         setRegion : function(region, animate){
8104             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8105             return this;
8106         },
8107
8108         /**
8109          * Appends an event handler
8110          *
8111          * @param {String}   eventName     The type of event to append
8112          * @param {Function} fn        The method the event invokes
8113          * @param {Object} scope       (optional) The scope (this object) of the fn
8114          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8115          */
8116         addListener : function(eventName, fn, scope, options){
8117             if (this.dom) {
8118                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8119             }
8120         },
8121
8122         /**
8123          * Removes an event handler from this element
8124          * @param {String} eventName the type of event to remove
8125          * @param {Function} fn the method the event invokes
8126          * @return {Roo.Element} this
8127          */
8128         removeListener : function(eventName, fn){
8129             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8130             return this;
8131         },
8132
8133         /**
8134          * Removes all previous added listeners from this element
8135          * @return {Roo.Element} this
8136          */
8137         removeAllListeners : function(){
8138             E.purgeElement(this.dom);
8139             return this;
8140         },
8141
8142         relayEvent : function(eventName, observable){
8143             this.on(eventName, function(e){
8144                 observable.fireEvent(eventName, e);
8145             });
8146         },
8147
8148         /**
8149          * Set the opacity of the element
8150          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154          setOpacity : function(opacity, animate){
8155             if(!animate || !A){
8156                 var s = this.dom.style;
8157                 if(Roo.isIE){
8158                     s.zoom = 1;
8159                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8160                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8161                 }else{
8162                     s.opacity = opacity;
8163                 }
8164             }else{
8165                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8166             }
8167             return this;
8168         },
8169
8170         /**
8171          * Gets the left X coordinate
8172          * @param {Boolean} local True to get the local css position instead of page coordinate
8173          * @return {Number}
8174          */
8175         getLeft : function(local){
8176             if(!local){
8177                 return this.getX();
8178             }else{
8179                 return parseInt(this.getStyle("left"), 10) || 0;
8180             }
8181         },
8182
8183         /**
8184          * Gets the right X coordinate of the element (element X position + element width)
8185          * @param {Boolean} local True to get the local css position instead of page coordinate
8186          * @return {Number}
8187          */
8188         getRight : function(local){
8189             if(!local){
8190                 return this.getX() + this.getWidth();
8191             }else{
8192                 return (this.getLeft(true) + this.getWidth()) || 0;
8193             }
8194         },
8195
8196         /**
8197          * Gets the top Y coordinate
8198          * @param {Boolean} local True to get the local css position instead of page coordinate
8199          * @return {Number}
8200          */
8201         getTop : function(local) {
8202             if(!local){
8203                 return this.getY();
8204             }else{
8205                 return parseInt(this.getStyle("top"), 10) || 0;
8206             }
8207         },
8208
8209         /**
8210          * Gets the bottom Y coordinate of the element (element Y position + element height)
8211          * @param {Boolean} local True to get the local css position instead of page coordinate
8212          * @return {Number}
8213          */
8214         getBottom : function(local){
8215             if(!local){
8216                 return this.getY() + this.getHeight();
8217             }else{
8218                 return (this.getTop(true) + this.getHeight()) || 0;
8219             }
8220         },
8221
8222         /**
8223         * Initializes positioning on this element. If a desired position is not passed, it will make the
8224         * the element positioned relative IF it is not already positioned.
8225         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8226         * @param {Number} zIndex (optional) The zIndex to apply
8227         * @param {Number} x (optional) Set the page X position
8228         * @param {Number} y (optional) Set the page Y position
8229         */
8230         position : function(pos, zIndex, x, y){
8231             if(!pos){
8232                if(this.getStyle('position') == 'static'){
8233                    this.setStyle('position', 'relative');
8234                }
8235             }else{
8236                 this.setStyle("position", pos);
8237             }
8238             if(zIndex){
8239                 this.setStyle("z-index", zIndex);
8240             }
8241             if(x !== undefined && y !== undefined){
8242                 this.setXY([x, y]);
8243             }else if(x !== undefined){
8244                 this.setX(x);
8245             }else if(y !== undefined){
8246                 this.setY(y);
8247             }
8248         },
8249
8250         /**
8251         * Clear positioning back to the default when the document was loaded
8252         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8253         * @return {Roo.Element} this
8254          */
8255         clearPositioning : function(value){
8256             value = value ||'';
8257             this.setStyle({
8258                 "left": value,
8259                 "right": value,
8260                 "top": value,
8261                 "bottom": value,
8262                 "z-index": "",
8263                 "position" : "static"
8264             });
8265             return this;
8266         },
8267
8268         /**
8269         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8270         * snapshot before performing an update and then restoring the element.
8271         * @return {Object}
8272         */
8273         getPositioning : function(){
8274             var l = this.getStyle("left");
8275             var t = this.getStyle("top");
8276             return {
8277                 "position" : this.getStyle("position"),
8278                 "left" : l,
8279                 "right" : l ? "" : this.getStyle("right"),
8280                 "top" : t,
8281                 "bottom" : t ? "" : this.getStyle("bottom"),
8282                 "z-index" : this.getStyle("z-index")
8283             };
8284         },
8285
8286         /**
8287          * Gets the width of the border(s) for the specified side(s)
8288          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8289          * passing lr would get the border (l)eft width + the border (r)ight width.
8290          * @return {Number} The width of the sides passed added together
8291          */
8292         getBorderWidth : function(side){
8293             return this.addStyles(side, El.borders);
8294         },
8295
8296         /**
8297          * Gets the width of the padding(s) for the specified side(s)
8298          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8299          * passing lr would get the padding (l)eft + the padding (r)ight.
8300          * @return {Number} The padding of the sides passed added together
8301          */
8302         getPadding : function(side){
8303             return this.addStyles(side, El.paddings);
8304         },
8305
8306         /**
8307         * Set positioning with an object returned by getPositioning().
8308         * @param {Object} posCfg
8309         * @return {Roo.Element} this
8310          */
8311         setPositioning : function(pc){
8312             this.applyStyles(pc);
8313             if(pc.right == "auto"){
8314                 this.dom.style.right = "";
8315             }
8316             if(pc.bottom == "auto"){
8317                 this.dom.style.bottom = "";
8318             }
8319             return this;
8320         },
8321
8322         // private
8323         fixDisplay : function(){
8324             if(this.getStyle("display") == "none"){
8325                 this.setStyle("visibility", "hidden");
8326                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8327                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8328                     this.setStyle("display", "block");
8329                 }
8330             }
8331         },
8332
8333         /**
8334          * Quick set left and top adding default units
8335          * @param {String} left The left CSS property value
8336          * @param {String} top The top CSS property value
8337          * @return {Roo.Element} this
8338          */
8339          setLeftTop : function(left, top){
8340             this.dom.style.left = this.addUnits(left);
8341             this.dom.style.top = this.addUnits(top);
8342             return this;
8343         },
8344
8345         /**
8346          * Move this element relative to its current position.
8347          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8348          * @param {Number} distance How far to move the element in pixels
8349          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8350          * @return {Roo.Element} this
8351          */
8352          move : function(direction, distance, animate){
8353             var xy = this.getXY();
8354             direction = direction.toLowerCase();
8355             switch(direction){
8356                 case "l":
8357                 case "left":
8358                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8359                     break;
8360                case "r":
8361                case "right":
8362                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8363                     break;
8364                case "t":
8365                case "top":
8366                case "up":
8367                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8368                     break;
8369                case "b":
8370                case "bottom":
8371                case "down":
8372                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8373                     break;
8374             }
8375             return this;
8376         },
8377
8378         /**
8379          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8380          * @return {Roo.Element} this
8381          */
8382         clip : function(){
8383             if(!this.isClipped){
8384                this.isClipped = true;
8385                this.originalClip = {
8386                    "o": this.getStyle("overflow"),
8387                    "x": this.getStyle("overflow-x"),
8388                    "y": this.getStyle("overflow-y")
8389                };
8390                this.setStyle("overflow", "hidden");
8391                this.setStyle("overflow-x", "hidden");
8392                this.setStyle("overflow-y", "hidden");
8393             }
8394             return this;
8395         },
8396
8397         /**
8398          *  Return clipping (overflow) to original clipping before clip() was called
8399          * @return {Roo.Element} this
8400          */
8401         unclip : function(){
8402             if(this.isClipped){
8403                 this.isClipped = false;
8404                 var o = this.originalClip;
8405                 if(o.o){this.setStyle("overflow", o.o);}
8406                 if(o.x){this.setStyle("overflow-x", o.x);}
8407                 if(o.y){this.setStyle("overflow-y", o.y);}
8408             }
8409             return this;
8410         },
8411
8412
8413         /**
8414          * Gets the x,y coordinates specified by the anchor position on the element.
8415          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8416          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8417          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8418          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8419          * @return {Array} [x, y] An array containing the element's x and y coordinates
8420          */
8421         getAnchorXY : function(anchor, local, s){
8422             //Passing a different size is useful for pre-calculating anchors,
8423             //especially for anchored animations that change the el size.
8424
8425             var w, h, vp = false;
8426             if(!s){
8427                 var d = this.dom;
8428                 if(d == document.body || d == document){
8429                     vp = true;
8430                     w = D.getViewWidth(); h = D.getViewHeight();
8431                 }else{
8432                     w = this.getWidth(); h = this.getHeight();
8433                 }
8434             }else{
8435                 w = s.width;  h = s.height;
8436             }
8437             var x = 0, y = 0, r = Math.round;
8438             switch((anchor || "tl").toLowerCase()){
8439                 case "c":
8440                     x = r(w*.5);
8441                     y = r(h*.5);
8442                 break;
8443                 case "t":
8444                     x = r(w*.5);
8445                     y = 0;
8446                 break;
8447                 case "l":
8448                     x = 0;
8449                     y = r(h*.5);
8450                 break;
8451                 case "r":
8452                     x = w;
8453                     y = r(h*.5);
8454                 break;
8455                 case "b":
8456                     x = r(w*.5);
8457                     y = h;
8458                 break;
8459                 case "tl":
8460                     x = 0;
8461                     y = 0;
8462                 break;
8463                 case "bl":
8464                     x = 0;
8465                     y = h;
8466                 break;
8467                 case "br":
8468                     x = w;
8469                     y = h;
8470                 break;
8471                 case "tr":
8472                     x = w;
8473                     y = 0;
8474                 break;
8475             }
8476             if(local === true){
8477                 return [x, y];
8478             }
8479             if(vp){
8480                 var sc = this.getScroll();
8481                 return [x + sc.left, y + sc.top];
8482             }
8483             //Add the element's offset xy
8484             var o = this.getXY();
8485             return [x+o[0], y+o[1]];
8486         },
8487
8488         /**
8489          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8490          * supported position values.
8491          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8492          * @param {String} position The position to align to.
8493          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8494          * @return {Array} [x, y]
8495          */
8496         getAlignToXY : function(el, p, o){
8497             el = Roo.get(el);
8498             var d = this.dom;
8499             if(!el.dom){
8500                 throw "Element.alignTo with an element that doesn't exist";
8501             }
8502             var c = false; //constrain to viewport
8503             var p1 = "", p2 = "";
8504             o = o || [0,0];
8505
8506             if(!p){
8507                 p = "tl-bl";
8508             }else if(p == "?"){
8509                 p = "tl-bl?";
8510             }else if(p.indexOf("-") == -1){
8511                 p = "tl-" + p;
8512             }
8513             p = p.toLowerCase();
8514             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8515             if(!m){
8516                throw "Element.alignTo with an invalid alignment " + p;
8517             }
8518             p1 = m[1]; p2 = m[2]; c = !!m[3];
8519
8520             //Subtract the aligned el's internal xy from the target's offset xy
8521             //plus custom offset to get the aligned el's new offset xy
8522             var a1 = this.getAnchorXY(p1, true);
8523             var a2 = el.getAnchorXY(p2, false);
8524             var x = a2[0] - a1[0] + o[0];
8525             var y = a2[1] - a1[1] + o[1];
8526             if(c){
8527                 //constrain the aligned el to viewport if necessary
8528                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8529                 // 5px of margin for ie
8530                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8531
8532                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8533                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8534                 //otherwise swap the aligned el to the opposite border of the target.
8535                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8536                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8537                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8538                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8539
8540                var doc = document;
8541                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8542                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8543
8544                if((x+w) > dw + scrollX){
8545                     x = swapX ? r.left-w : dw+scrollX-w;
8546                 }
8547                if(x < scrollX){
8548                    x = swapX ? r.right : scrollX;
8549                }
8550                if((y+h) > dh + scrollY){
8551                     y = swapY ? r.top-h : dh+scrollY-h;
8552                 }
8553                if (y < scrollY){
8554                    y = swapY ? r.bottom : scrollY;
8555                }
8556             }
8557             return [x,y];
8558         },
8559
8560         // private
8561         getConstrainToXY : function(){
8562             var os = {top:0, left:0, bottom:0, right: 0};
8563
8564             return function(el, local, offsets, proposedXY){
8565                 el = Roo.get(el);
8566                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8567
8568                 var vw, vh, vx = 0, vy = 0;
8569                 if(el.dom == document.body || el.dom == document){
8570                     vw = Roo.lib.Dom.getViewWidth();
8571                     vh = Roo.lib.Dom.getViewHeight();
8572                 }else{
8573                     vw = el.dom.clientWidth;
8574                     vh = el.dom.clientHeight;
8575                     if(!local){
8576                         var vxy = el.getXY();
8577                         vx = vxy[0];
8578                         vy = vxy[1];
8579                     }
8580                 }
8581
8582                 var s = el.getScroll();
8583
8584                 vx += offsets.left + s.left;
8585                 vy += offsets.top + s.top;
8586
8587                 vw -= offsets.right;
8588                 vh -= offsets.bottom;
8589
8590                 var vr = vx+vw;
8591                 var vb = vy+vh;
8592
8593                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8594                 var x = xy[0], y = xy[1];
8595                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8596
8597                 // only move it if it needs it
8598                 var moved = false;
8599
8600                 // first validate right/bottom
8601                 if((x + w) > vr){
8602                     x = vr - w;
8603                     moved = true;
8604                 }
8605                 if((y + h) > vb){
8606                     y = vb - h;
8607                     moved = true;
8608                 }
8609                 // then make sure top/left isn't negative
8610                 if(x < vx){
8611                     x = vx;
8612                     moved = true;
8613                 }
8614                 if(y < vy){
8615                     y = vy;
8616                     moved = true;
8617                 }
8618                 return moved ? [x, y] : false;
8619             };
8620         }(),
8621
8622         // private
8623         adjustForConstraints : function(xy, parent, offsets){
8624             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8625         },
8626
8627         /**
8628          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8629          * document it aligns it to the viewport.
8630          * The position parameter is optional, and can be specified in any one of the following formats:
8631          * <ul>
8632          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8633          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8634          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8635          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8636          *   <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
8637          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8638          * </ul>
8639          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8640          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8641          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8642          * that specified in order to enforce the viewport constraints.
8643          * Following are all of the supported anchor positions:
8644     <pre>
8645     Value  Description
8646     -----  -----------------------------
8647     tl     The top left corner (default)
8648     t      The center of the top edge
8649     tr     The top right corner
8650     l      The center of the left edge
8651     c      In the center of the element
8652     r      The center of the right edge
8653     bl     The bottom left corner
8654     b      The center of the bottom edge
8655     br     The bottom right corner
8656     </pre>
8657     Example Usage:
8658     <pre><code>
8659     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8660     el.alignTo("other-el");
8661
8662     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8663     el.alignTo("other-el", "tr?");
8664
8665     // align the bottom right corner of el with the center left edge of other-el
8666     el.alignTo("other-el", "br-l?");
8667
8668     // align the center of el with the bottom left corner of other-el and
8669     // adjust the x position by -6 pixels (and the y position by 0)
8670     el.alignTo("other-el", "c-bl", [-6, 0]);
8671     </code></pre>
8672          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8673          * @param {String} position The position to align to.
8674          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8676          * @return {Roo.Element} this
8677          */
8678         alignTo : function(element, position, offsets, animate){
8679             var xy = this.getAlignToXY(element, position, offsets);
8680             this.setXY(xy, this.preanim(arguments, 3));
8681             return this;
8682         },
8683
8684         /**
8685          * Anchors an element to another element and realigns it when the window is resized.
8686          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8687          * @param {String} position The position to align to.
8688          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8689          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8690          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8691          * is a number, it is used as the buffer delay (defaults to 50ms).
8692          * @param {Function} callback The function to call after the animation finishes
8693          * @return {Roo.Element} this
8694          */
8695         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8696             var action = function(){
8697                 this.alignTo(el, alignment, offsets, animate);
8698                 Roo.callback(callback, this);
8699             };
8700             Roo.EventManager.onWindowResize(action, this);
8701             var tm = typeof monitorScroll;
8702             if(tm != 'undefined'){
8703                 Roo.EventManager.on(window, 'scroll', action, this,
8704                     {buffer: tm == 'number' ? monitorScroll : 50});
8705             }
8706             action.call(this); // align immediately
8707             return this;
8708         },
8709         /**
8710          * Clears any opacity settings from this element. Required in some cases for IE.
8711          * @return {Roo.Element} this
8712          */
8713         clearOpacity : function(){
8714             if (window.ActiveXObject) {
8715                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8716                     this.dom.style.filter = "";
8717                 }
8718             } else {
8719                 this.dom.style.opacity = "";
8720                 this.dom.style["-moz-opacity"] = "";
8721                 this.dom.style["-khtml-opacity"] = "";
8722             }
8723             return this;
8724         },
8725
8726         /**
8727          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8729          * @return {Roo.Element} this
8730          */
8731         hide : function(animate){
8732             this.setVisible(false, this.preanim(arguments, 0));
8733             return this;
8734         },
8735
8736         /**
8737         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8738         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8739          * @return {Roo.Element} this
8740          */
8741         show : function(animate){
8742             this.setVisible(true, this.preanim(arguments, 0));
8743             return this;
8744         },
8745
8746         /**
8747          * @private Test if size has a unit, otherwise appends the default
8748          */
8749         addUnits : function(size){
8750             return Roo.Element.addUnits(size, this.defaultUnit);
8751         },
8752
8753         /**
8754          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8755          * @return {Roo.Element} this
8756          */
8757         beginMeasure : function(){
8758             var el = this.dom;
8759             if(el.offsetWidth || el.offsetHeight){
8760                 return this; // offsets work already
8761             }
8762             var changed = [];
8763             var p = this.dom, b = document.body; // start with this element
8764             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8765                 var pe = Roo.get(p);
8766                 if(pe.getStyle('display') == 'none'){
8767                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8768                     p.style.visibility = "hidden";
8769                     p.style.display = "block";
8770                 }
8771                 p = p.parentNode;
8772             }
8773             this._measureChanged = changed;
8774             return this;
8775
8776         },
8777
8778         /**
8779          * Restores displays to before beginMeasure was called
8780          * @return {Roo.Element} this
8781          */
8782         endMeasure : function(){
8783             var changed = this._measureChanged;
8784             if(changed){
8785                 for(var i = 0, len = changed.length; i < len; i++) {
8786                     var r = changed[i];
8787                     r.el.style.visibility = r.visibility;
8788                     r.el.style.display = "none";
8789                 }
8790                 this._measureChanged = null;
8791             }
8792             return this;
8793         },
8794
8795         /**
8796         * Update the innerHTML of this element, optionally searching for and processing scripts
8797         * @param {String} html The new HTML
8798         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8799         * @param {Function} callback For async script loading you can be noticed when the update completes
8800         * @return {Roo.Element} this
8801          */
8802         update : function(html, loadScripts, callback){
8803             if(typeof html == "undefined"){
8804                 html = "";
8805             }
8806             if(loadScripts !== true){
8807                 this.dom.innerHTML = html;
8808                 if(typeof callback == "function"){
8809                     callback();
8810                 }
8811                 return this;
8812             }
8813             var id = Roo.id();
8814             var dom = this.dom;
8815
8816             html += '<span id="' + id + '"></span>';
8817
8818             E.onAvailable(id, function(){
8819                 var hd = document.getElementsByTagName("head")[0];
8820                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8821                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8822                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8823
8824                 var match;
8825                 while(match = re.exec(html)){
8826                     var attrs = match[1];
8827                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8828                     if(srcMatch && srcMatch[2]){
8829                        var s = document.createElement("script");
8830                        s.src = srcMatch[2];
8831                        var typeMatch = attrs.match(typeRe);
8832                        if(typeMatch && typeMatch[2]){
8833                            s.type = typeMatch[2];
8834                        }
8835                        hd.appendChild(s);
8836                     }else if(match[2] && match[2].length > 0){
8837                         if(window.execScript) {
8838                            window.execScript(match[2]);
8839                         } else {
8840                             /**
8841                              * eval:var:id
8842                              * eval:var:dom
8843                              * eval:var:html
8844                              * 
8845                              */
8846                            window.eval(match[2]);
8847                         }
8848                     }
8849                 }
8850                 var el = document.getElementById(id);
8851                 if(el){el.parentNode.removeChild(el);}
8852                 if(typeof callback == "function"){
8853                     callback();
8854                 }
8855             });
8856             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8857             return this;
8858         },
8859
8860         /**
8861          * Direct access to the UpdateManager update() method (takes the same parameters).
8862          * @param {String/Function} url The url for this request or a function to call to get the url
8863          * @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}
8864          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8865          * @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.
8866          * @return {Roo.Element} this
8867          */
8868         load : function(){
8869             var um = this.getUpdateManager();
8870             um.update.apply(um, arguments);
8871             return this;
8872         },
8873
8874         /**
8875         * Gets this element's UpdateManager
8876         * @return {Roo.UpdateManager} The UpdateManager
8877         */
8878         getUpdateManager : function(){
8879             if(!this.updateManager){
8880                 this.updateManager = new Roo.UpdateManager(this);
8881             }
8882             return this.updateManager;
8883         },
8884
8885         /**
8886          * Disables text selection for this element (normalized across browsers)
8887          * @return {Roo.Element} this
8888          */
8889         unselectable : function(){
8890             this.dom.unselectable = "on";
8891             this.swallowEvent("selectstart", true);
8892             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8893             this.addClass("x-unselectable");
8894             return this;
8895         },
8896
8897         /**
8898         * Calculates the x, y to center this element on the screen
8899         * @return {Array} The x, y values [x, y]
8900         */
8901         getCenterXY : function(){
8902             return this.getAlignToXY(document, 'c-c');
8903         },
8904
8905         /**
8906         * Centers the Element in either the viewport, or another Element.
8907         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8908         */
8909         center : function(centerIn){
8910             this.alignTo(centerIn || document, 'c-c');
8911             return this;
8912         },
8913
8914         /**
8915          * Tests various css rules/browsers to determine if this element uses a border box
8916          * @return {Boolean}
8917          */
8918         isBorderBox : function(){
8919             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8920         },
8921
8922         /**
8923          * Return a box {x, y, width, height} that can be used to set another elements
8924          * size/location to match this element.
8925          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8926          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8927          * @return {Object} box An object in the format {x, y, width, height}
8928          */
8929         getBox : function(contentBox, local){
8930             var xy;
8931             if(!local){
8932                 xy = this.getXY();
8933             }else{
8934                 var left = parseInt(this.getStyle("left"), 10) || 0;
8935                 var top = parseInt(this.getStyle("top"), 10) || 0;
8936                 xy = [left, top];
8937             }
8938             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8939             if(!contentBox){
8940                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8941             }else{
8942                 var l = this.getBorderWidth("l")+this.getPadding("l");
8943                 var r = this.getBorderWidth("r")+this.getPadding("r");
8944                 var t = this.getBorderWidth("t")+this.getPadding("t");
8945                 var b = this.getBorderWidth("b")+this.getPadding("b");
8946                 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)};
8947             }
8948             bx.right = bx.x + bx.width;
8949             bx.bottom = bx.y + bx.height;
8950             return bx;
8951         },
8952
8953         /**
8954          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8955          for more information about the sides.
8956          * @param {String} sides
8957          * @return {Number}
8958          */
8959         getFrameWidth : function(sides, onlyContentBox){
8960             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8961         },
8962
8963         /**
8964          * 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.
8965          * @param {Object} box The box to fill {x, y, width, height}
8966          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8968          * @return {Roo.Element} this
8969          */
8970         setBox : function(box, adjust, animate){
8971             var w = box.width, h = box.height;
8972             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8973                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8974                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8975             }
8976             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8977             return this;
8978         },
8979
8980         /**
8981          * Forces the browser to repaint this element
8982          * @return {Roo.Element} this
8983          */
8984          repaint : function(){
8985             var dom = this.dom;
8986             this.addClass("x-repaint");
8987             setTimeout(function(){
8988                 Roo.get(dom).removeClass("x-repaint");
8989             }, 1);
8990             return this;
8991         },
8992
8993         /**
8994          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8995          * then it returns the calculated width of the sides (see getPadding)
8996          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8997          * @return {Object/Number}
8998          */
8999         getMargins : function(side){
9000             if(!side){
9001                 return {
9002                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9003                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9004                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9005                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9006                 };
9007             }else{
9008                 return this.addStyles(side, El.margins);
9009              }
9010         },
9011
9012         // private
9013         addStyles : function(sides, styles){
9014             var val = 0, v, w;
9015             for(var i = 0, len = sides.length; i < len; i++){
9016                 v = this.getStyle(styles[sides.charAt(i)]);
9017                 if(v){
9018                      w = parseInt(v, 10);
9019                      if(w){ val += w; }
9020                 }
9021             }
9022             return val;
9023         },
9024
9025         /**
9026          * Creates a proxy element of this element
9027          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9028          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9029          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9030          * @return {Roo.Element} The new proxy element
9031          */
9032         createProxy : function(config, renderTo, matchBox){
9033             if(renderTo){
9034                 renderTo = Roo.getDom(renderTo);
9035             }else{
9036                 renderTo = document.body;
9037             }
9038             config = typeof config == "object" ?
9039                 config : {tag : "div", cls: config};
9040             var proxy = Roo.DomHelper.append(renderTo, config, true);
9041             if(matchBox){
9042                proxy.setBox(this.getBox());
9043             }
9044             return proxy;
9045         },
9046
9047         /**
9048          * Puts a mask over this element to disable user interaction. Requires core.css.
9049          * This method can only be applied to elements which accept child nodes.
9050          * @param {String} msg (optional) A message to display in the mask
9051          * @param {String} msgCls (optional) A css class to apply to the msg element
9052          * @return {Element} The mask  element
9053          */
9054         mask : function(msg, msgCls)
9055         {
9056             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9057                 this.setStyle("position", "relative");
9058             }
9059             if(!this._mask){
9060                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9061             }
9062             this.addClass("x-masked");
9063             this._mask.setDisplayed(true);
9064             
9065             // we wander
9066             var z = 0;
9067             var dom = this.dom;
9068             while (dom && dom.style) {
9069                 if (!isNaN(parseInt(dom.style.zIndex))) {
9070                     z = Math.max(z, parseInt(dom.style.zIndex));
9071                 }
9072                 dom = dom.parentNode;
9073             }
9074             // if we are masking the body - then it hides everything..
9075             if (this.dom == document.body) {
9076                 z = 1000000;
9077                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9078                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9079             }
9080            
9081             if(typeof msg == 'string'){
9082                 if(!this._maskMsg){
9083                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9084                 }
9085                 var mm = this._maskMsg;
9086                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9087                 if (mm.dom.firstChild) { // weird IE issue?
9088                     mm.dom.firstChild.innerHTML = msg;
9089                 }
9090                 mm.setDisplayed(true);
9091                 mm.center(this);
9092                 mm.setStyle('z-index', z + 102);
9093             }
9094             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9095                 this._mask.setHeight(this.getHeight());
9096             }
9097             this._mask.setStyle('z-index', z + 100);
9098             
9099             return this._mask;
9100         },
9101
9102         /**
9103          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9104          * it is cached for reuse.
9105          */
9106         unmask : function(removeEl){
9107             if(this._mask){
9108                 if(removeEl === true){
9109                     this._mask.remove();
9110                     delete this._mask;
9111                     if(this._maskMsg){
9112                         this._maskMsg.remove();
9113                         delete this._maskMsg;
9114                     }
9115                 }else{
9116                     this._mask.setDisplayed(false);
9117                     if(this._maskMsg){
9118                         this._maskMsg.setDisplayed(false);
9119                     }
9120                 }
9121             }
9122             this.removeClass("x-masked");
9123         },
9124
9125         /**
9126          * Returns true if this element is masked
9127          * @return {Boolean}
9128          */
9129         isMasked : function(){
9130             return this._mask && this._mask.isVisible();
9131         },
9132
9133         /**
9134          * Creates an iframe shim for this element to keep selects and other windowed objects from
9135          * showing through.
9136          * @return {Roo.Element} The new shim element
9137          */
9138         createShim : function(){
9139             var el = document.createElement('iframe');
9140             el.frameBorder = 'no';
9141             el.className = 'roo-shim';
9142             if(Roo.isIE && Roo.isSecure){
9143                 el.src = Roo.SSL_SECURE_URL;
9144             }
9145             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9146             shim.autoBoxAdjust = false;
9147             return shim;
9148         },
9149
9150         /**
9151          * Removes this element from the DOM and deletes it from the cache
9152          */
9153         remove : function(){
9154             if(this.dom.parentNode){
9155                 this.dom.parentNode.removeChild(this.dom);
9156             }
9157             delete El.cache[this.dom.id];
9158         },
9159
9160         /**
9161          * Sets up event handlers to add and remove a css class when the mouse is over this element
9162          * @param {String} className
9163          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9164          * mouseout events for children elements
9165          * @return {Roo.Element} this
9166          */
9167         addClassOnOver : function(className, preventFlicker){
9168             this.on("mouseover", function(){
9169                 Roo.fly(this, '_internal').addClass(className);
9170             }, this.dom);
9171             var removeFn = function(e){
9172                 if(preventFlicker !== true || !e.within(this, true)){
9173                     Roo.fly(this, '_internal').removeClass(className);
9174                 }
9175             };
9176             this.on("mouseout", removeFn, this.dom);
9177             return this;
9178         },
9179
9180         /**
9181          * Sets up event handlers to add and remove a css class when this element has the focus
9182          * @param {String} className
9183          * @return {Roo.Element} this
9184          */
9185         addClassOnFocus : function(className){
9186             this.on("focus", function(){
9187                 Roo.fly(this, '_internal').addClass(className);
9188             }, this.dom);
9189             this.on("blur", function(){
9190                 Roo.fly(this, '_internal').removeClass(className);
9191             }, this.dom);
9192             return this;
9193         },
9194         /**
9195          * 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)
9196          * @param {String} className
9197          * @return {Roo.Element} this
9198          */
9199         addClassOnClick : function(className){
9200             var dom = this.dom;
9201             this.on("mousedown", function(){
9202                 Roo.fly(dom, '_internal').addClass(className);
9203                 var d = Roo.get(document);
9204                 var fn = function(){
9205                     Roo.fly(dom, '_internal').removeClass(className);
9206                     d.removeListener("mouseup", fn);
9207                 };
9208                 d.on("mouseup", fn);
9209             });
9210             return this;
9211         },
9212
9213         /**
9214          * Stops the specified event from bubbling and optionally prevents the default action
9215          * @param {String} eventName
9216          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9217          * @return {Roo.Element} this
9218          */
9219         swallowEvent : function(eventName, preventDefault){
9220             var fn = function(e){
9221                 e.stopPropagation();
9222                 if(preventDefault){
9223                     e.preventDefault();
9224                 }
9225             };
9226             if(eventName instanceof Array){
9227                 for(var i = 0, len = eventName.length; i < len; i++){
9228                      this.on(eventName[i], fn);
9229                 }
9230                 return this;
9231             }
9232             this.on(eventName, fn);
9233             return this;
9234         },
9235
9236         /**
9237          * @private
9238          */
9239       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9240
9241         /**
9242          * Sizes this element to its parent element's dimensions performing
9243          * neccessary box adjustments.
9244          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9245          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9246          * @return {Roo.Element} this
9247          */
9248         fitToParent : function(monitorResize, targetParent) {
9249           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9250           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9251           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9252             return;
9253           }
9254           var p = Roo.get(targetParent || this.dom.parentNode);
9255           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9256           if (monitorResize === true) {
9257             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9258             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9259           }
9260           return this;
9261         },
9262
9263         /**
9264          * Gets the next sibling, skipping text nodes
9265          * @return {HTMLElement} The next sibling or null
9266          */
9267         getNextSibling : function(){
9268             var n = this.dom.nextSibling;
9269             while(n && n.nodeType != 1){
9270                 n = n.nextSibling;
9271             }
9272             return n;
9273         },
9274
9275         /**
9276          * Gets the previous sibling, skipping text nodes
9277          * @return {HTMLElement} The previous sibling or null
9278          */
9279         getPrevSibling : function(){
9280             var n = this.dom.previousSibling;
9281             while(n && n.nodeType != 1){
9282                 n = n.previousSibling;
9283             }
9284             return n;
9285         },
9286
9287
9288         /**
9289          * Appends the passed element(s) to this element
9290          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9291          * @return {Roo.Element} this
9292          */
9293         appendChild: function(el){
9294             el = Roo.get(el);
9295             el.appendTo(this);
9296             return this;
9297         },
9298
9299         /**
9300          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9301          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9302          * automatically generated with the specified attributes.
9303          * @param {HTMLElement} insertBefore (optional) a child element of this element
9304          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9305          * @return {Roo.Element} The new child element
9306          */
9307         createChild: function(config, insertBefore, returnDom){
9308             config = config || {tag:'div'};
9309             if(insertBefore){
9310                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9311             }
9312             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9313         },
9314
9315         /**
9316          * Appends this element to the passed element
9317          * @param {String/HTMLElement/Element} el The new parent element
9318          * @return {Roo.Element} this
9319          */
9320         appendTo: function(el){
9321             el = Roo.getDom(el);
9322             el.appendChild(this.dom);
9323             return this;
9324         },
9325
9326         /**
9327          * Inserts this element before the passed element in the DOM
9328          * @param {String/HTMLElement/Element} el The element to insert before
9329          * @return {Roo.Element} this
9330          */
9331         insertBefore: function(el){
9332             el = Roo.getDom(el);
9333             el.parentNode.insertBefore(this.dom, el);
9334             return this;
9335         },
9336
9337         /**
9338          * Inserts this element after the passed element in the DOM
9339          * @param {String/HTMLElement/Element} el The element to insert after
9340          * @return {Roo.Element} this
9341          */
9342         insertAfter: function(el){
9343             el = Roo.getDom(el);
9344             el.parentNode.insertBefore(this.dom, el.nextSibling);
9345             return this;
9346         },
9347
9348         /**
9349          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9350          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9351          * @return {Roo.Element} The new child
9352          */
9353         insertFirst: function(el, returnDom){
9354             el = el || {};
9355             if(typeof el == 'object' && !el.nodeType){ // dh config
9356                 return this.createChild(el, this.dom.firstChild, returnDom);
9357             }else{
9358                 el = Roo.getDom(el);
9359                 this.dom.insertBefore(el, this.dom.firstChild);
9360                 return !returnDom ? Roo.get(el) : el;
9361             }
9362         },
9363
9364         /**
9365          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9366          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9367          * @param {String} where (optional) 'before' or 'after' defaults to before
9368          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9369          * @return {Roo.Element} the inserted Element
9370          */
9371         insertSibling: function(el, where, returnDom){
9372             where = where ? where.toLowerCase() : 'before';
9373             el = el || {};
9374             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9375
9376             if(typeof el == 'object' && !el.nodeType){ // dh config
9377                 if(where == 'after' && !this.dom.nextSibling){
9378                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9379                 }else{
9380                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9381                 }
9382
9383             }else{
9384                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9385                             where == 'before' ? this.dom : this.dom.nextSibling);
9386                 if(!returnDom){
9387                     rt = Roo.get(rt);
9388                 }
9389             }
9390             return rt;
9391         },
9392
9393         /**
9394          * Creates and wraps this element with another element
9395          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9396          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9397          * @return {HTMLElement/Element} The newly created wrapper element
9398          */
9399         wrap: function(config, returnDom){
9400             if(!config){
9401                 config = {tag: "div"};
9402             }
9403             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9404             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9405             return newEl;
9406         },
9407
9408         /**
9409          * Replaces the passed element with this element
9410          * @param {String/HTMLElement/Element} el The element to replace
9411          * @return {Roo.Element} this
9412          */
9413         replace: function(el){
9414             el = Roo.get(el);
9415             this.insertBefore(el);
9416             el.remove();
9417             return this;
9418         },
9419
9420         /**
9421          * Inserts an html fragment into this element
9422          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9423          * @param {String} html The HTML fragment
9424          * @param {Boolean} returnEl True to return an Roo.Element
9425          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9426          */
9427         insertHtml : function(where, html, returnEl){
9428             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9429             return returnEl ? Roo.get(el) : el;
9430         },
9431
9432         /**
9433          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9434          * @param {Object} o The object with the attributes
9435          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9436          * @return {Roo.Element} this
9437          */
9438         set : function(o, useSet){
9439             var el = this.dom;
9440             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9441             for(var attr in o){
9442                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9443                 if(attr=="cls"){
9444                     el.className = o["cls"];
9445                 }else{
9446                     if(useSet) {
9447                         el.setAttribute(attr, o[attr]);
9448                     } else {
9449                         el[attr] = o[attr];
9450                     }
9451                 }
9452             }
9453             if(o.style){
9454                 Roo.DomHelper.applyStyles(el, o.style);
9455             }
9456             return this;
9457         },
9458
9459         /**
9460          * Convenience method for constructing a KeyMap
9461          * @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:
9462          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9463          * @param {Function} fn The function to call
9464          * @param {Object} scope (optional) The scope of the function
9465          * @return {Roo.KeyMap} The KeyMap created
9466          */
9467         addKeyListener : function(key, fn, scope){
9468             var config;
9469             if(typeof key != "object" || key instanceof Array){
9470                 config = {
9471                     key: key,
9472                     fn: fn,
9473                     scope: scope
9474                 };
9475             }else{
9476                 config = {
9477                     key : key.key,
9478                     shift : key.shift,
9479                     ctrl : key.ctrl,
9480                     alt : key.alt,
9481                     fn: fn,
9482                     scope: scope
9483                 };
9484             }
9485             return new Roo.KeyMap(this, config);
9486         },
9487
9488         /**
9489          * Creates a KeyMap for this element
9490          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9491          * @return {Roo.KeyMap} The KeyMap created
9492          */
9493         addKeyMap : function(config){
9494             return new Roo.KeyMap(this, config);
9495         },
9496
9497         /**
9498          * Returns true if this element is scrollable.
9499          * @return {Boolean}
9500          */
9501          isScrollable : function(){
9502             var dom = this.dom;
9503             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9504         },
9505
9506         /**
9507          * 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().
9508          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9509          * @param {Number} value The new scroll value
9510          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9511          * @return {Element} this
9512          */
9513
9514         scrollTo : function(side, value, animate){
9515             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9516             if(!animate || !A){
9517                 this.dom[prop] = value;
9518             }else{
9519                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9520                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9521             }
9522             return this;
9523         },
9524
9525         /**
9526          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9527          * within this element's scrollable range.
9528          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9529          * @param {Number} distance How far to scroll the element in pixels
9530          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9531          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9532          * was scrolled as far as it could go.
9533          */
9534          scroll : function(direction, distance, animate){
9535              if(!this.isScrollable()){
9536                  return;
9537              }
9538              var el = this.dom;
9539              var l = el.scrollLeft, t = el.scrollTop;
9540              var w = el.scrollWidth, h = el.scrollHeight;
9541              var cw = el.clientWidth, ch = el.clientHeight;
9542              direction = direction.toLowerCase();
9543              var scrolled = false;
9544              var a = this.preanim(arguments, 2);
9545              switch(direction){
9546                  case "l":
9547                  case "left":
9548                      if(w - l > cw){
9549                          var v = Math.min(l + distance, w-cw);
9550                          this.scrollTo("left", v, a);
9551                          scrolled = true;
9552                      }
9553                      break;
9554                 case "r":
9555                 case "right":
9556                      if(l > 0){
9557                          var v = Math.max(l - distance, 0);
9558                          this.scrollTo("left", v, a);
9559                          scrolled = true;
9560                      }
9561                      break;
9562                 case "t":
9563                 case "top":
9564                 case "up":
9565                      if(t > 0){
9566                          var v = Math.max(t - distance, 0);
9567                          this.scrollTo("top", v, a);
9568                          scrolled = true;
9569                      }
9570                      break;
9571                 case "b":
9572                 case "bottom":
9573                 case "down":
9574                      if(h - t > ch){
9575                          var v = Math.min(t + distance, h-ch);
9576                          this.scrollTo("top", v, a);
9577                          scrolled = true;
9578                      }
9579                      break;
9580              }
9581              return scrolled;
9582         },
9583
9584         /**
9585          * Translates the passed page coordinates into left/top css values for this element
9586          * @param {Number/Array} x The page x or an array containing [x, y]
9587          * @param {Number} y The page y
9588          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9589          */
9590         translatePoints : function(x, y){
9591             if(typeof x == 'object' || x instanceof Array){
9592                 y = x[1]; x = x[0];
9593             }
9594             var p = this.getStyle('position');
9595             var o = this.getXY();
9596
9597             var l = parseInt(this.getStyle('left'), 10);
9598             var t = parseInt(this.getStyle('top'), 10);
9599
9600             if(isNaN(l)){
9601                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9602             }
9603             if(isNaN(t)){
9604                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9605             }
9606
9607             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9608         },
9609
9610         /**
9611          * Returns the current scroll position of the element.
9612          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9613          */
9614         getScroll : function(){
9615             var d = this.dom, doc = document;
9616             if(d == doc || d == doc.body){
9617                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9618                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9619                 return {left: l, top: t};
9620             }else{
9621                 return {left: d.scrollLeft, top: d.scrollTop};
9622             }
9623         },
9624
9625         /**
9626          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9627          * are convert to standard 6 digit hex color.
9628          * @param {String} attr The css attribute
9629          * @param {String} defaultValue The default value to use when a valid color isn't found
9630          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9631          * YUI color anims.
9632          */
9633         getColor : function(attr, defaultValue, prefix){
9634             var v = this.getStyle(attr);
9635             if(!v || v == "transparent" || v == "inherit") {
9636                 return defaultValue;
9637             }
9638             var color = typeof prefix == "undefined" ? "#" : prefix;
9639             if(v.substr(0, 4) == "rgb("){
9640                 var rvs = v.slice(4, v.length -1).split(",");
9641                 for(var i = 0; i < 3; i++){
9642                     var h = parseInt(rvs[i]).toString(16);
9643                     if(h < 16){
9644                         h = "0" + h;
9645                     }
9646                     color += h;
9647                 }
9648             } else {
9649                 if(v.substr(0, 1) == "#"){
9650                     if(v.length == 4) {
9651                         for(var i = 1; i < 4; i++){
9652                             var c = v.charAt(i);
9653                             color +=  c + c;
9654                         }
9655                     }else if(v.length == 7){
9656                         color += v.substr(1);
9657                     }
9658                 }
9659             }
9660             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9661         },
9662
9663         /**
9664          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9665          * gradient background, rounded corners and a 4-way shadow.
9666          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9667          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9668          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9669          * @return {Roo.Element} this
9670          */
9671         boxWrap : function(cls){
9672             cls = cls || 'x-box';
9673             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9674             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9675             return el;
9676         },
9677
9678         /**
9679          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9680          * @param {String} namespace The namespace in which to look for the attribute
9681          * @param {String} name The attribute name
9682          * @return {String} The attribute value
9683          */
9684         getAttributeNS : Roo.isIE ? function(ns, name){
9685             var d = this.dom;
9686             var type = typeof d[ns+":"+name];
9687             if(type != 'undefined' && type != 'unknown'){
9688                 return d[ns+":"+name];
9689             }
9690             return d[name];
9691         } : function(ns, name){
9692             var d = this.dom;
9693             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9694         },
9695         
9696         
9697         /**
9698          * Sets or Returns the value the dom attribute value
9699          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9700          * @param {String} value (optional) The value to set the attribute to
9701          * @return {String} The attribute value
9702          */
9703         attr : function(name){
9704             if (arguments.length > 1) {
9705                 this.dom.setAttribute(name, arguments[1]);
9706                 return arguments[1];
9707             }
9708             if (typeof(name) == 'object') {
9709                 for(var i in name) {
9710                     this.attr(i, name[i]);
9711                 }
9712                 return name;
9713             }
9714             
9715             
9716             if (!this.dom.hasAttribute(name)) {
9717                 return undefined;
9718             }
9719             return this.dom.getAttribute(name);
9720         }
9721         
9722         
9723         
9724     };
9725
9726     var ep = El.prototype;
9727
9728     /**
9729      * Appends an event handler (Shorthand for addListener)
9730      * @param {String}   eventName     The type of event to append
9731      * @param {Function} fn        The method the event invokes
9732      * @param {Object} scope       (optional) The scope (this object) of the fn
9733      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9734      * @method
9735      */
9736     ep.on = ep.addListener;
9737         // backwards compat
9738     ep.mon = ep.addListener;
9739
9740     /**
9741      * Removes an event handler from this element (shorthand for removeListener)
9742      * @param {String} eventName the type of event to remove
9743      * @param {Function} fn the method the event invokes
9744      * @return {Roo.Element} this
9745      * @method
9746      */
9747     ep.un = ep.removeListener;
9748
9749     /**
9750      * true to automatically adjust width and height settings for box-model issues (default to true)
9751      */
9752     ep.autoBoxAdjust = true;
9753
9754     // private
9755     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9756
9757     // private
9758     El.addUnits = function(v, defaultUnit){
9759         if(v === "" || v == "auto"){
9760             return v;
9761         }
9762         if(v === undefined){
9763             return '';
9764         }
9765         if(typeof v == "number" || !El.unitPattern.test(v)){
9766             return v + (defaultUnit || 'px');
9767         }
9768         return v;
9769     };
9770
9771     // special markup used throughout Roo when box wrapping elements
9772     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>';
9773     /**
9774      * Visibility mode constant - Use visibility to hide element
9775      * @static
9776      * @type Number
9777      */
9778     El.VISIBILITY = 1;
9779     /**
9780      * Visibility mode constant - Use display to hide element
9781      * @static
9782      * @type Number
9783      */
9784     El.DISPLAY = 2;
9785
9786     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9787     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9788     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9789
9790
9791
9792     /**
9793      * @private
9794      */
9795     El.cache = {};
9796
9797     var docEl;
9798
9799     /**
9800      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9801      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9802      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9803      * @return {Element} The Element object
9804      * @static
9805      */
9806     El.get = function(el){
9807         var ex, elm, id;
9808         if(!el){ return null; }
9809         if(typeof el == "string"){ // element id
9810             if(!(elm = document.getElementById(el))){
9811                 return null;
9812             }
9813             if(ex = El.cache[el]){
9814                 ex.dom = elm;
9815             }else{
9816                 ex = El.cache[el] = new El(elm);
9817             }
9818             return ex;
9819         }else if(el.tagName){ // dom element
9820             if(!(id = el.id)){
9821                 id = Roo.id(el);
9822             }
9823             if(ex = El.cache[id]){
9824                 ex.dom = el;
9825             }else{
9826                 ex = El.cache[id] = new El(el);
9827             }
9828             return ex;
9829         }else if(el instanceof El){
9830             if(el != docEl){
9831                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9832                                                               // catch case where it hasn't been appended
9833                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9834             }
9835             return el;
9836         }else if(el.isComposite){
9837             return el;
9838         }else if(el instanceof Array){
9839             return El.select(el);
9840         }else if(el == document){
9841             // create a bogus element object representing the document object
9842             if(!docEl){
9843                 var f = function(){};
9844                 f.prototype = El.prototype;
9845                 docEl = new f();
9846                 docEl.dom = document;
9847             }
9848             return docEl;
9849         }
9850         return null;
9851     };
9852
9853     // private
9854     El.uncache = function(el){
9855         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9856             if(a[i]){
9857                 delete El.cache[a[i].id || a[i]];
9858             }
9859         }
9860     };
9861
9862     // private
9863     // Garbage collection - uncache elements/purge listeners on orphaned elements
9864     // so we don't hold a reference and cause the browser to retain them
9865     El.garbageCollect = function(){
9866         if(!Roo.enableGarbageCollector){
9867             clearInterval(El.collectorThread);
9868             return;
9869         }
9870         for(var eid in El.cache){
9871             var el = El.cache[eid], d = el.dom;
9872             // -------------------------------------------------------
9873             // Determining what is garbage:
9874             // -------------------------------------------------------
9875             // !d
9876             // dom node is null, definitely garbage
9877             // -------------------------------------------------------
9878             // !d.parentNode
9879             // no parentNode == direct orphan, definitely garbage
9880             // -------------------------------------------------------
9881             // !d.offsetParent && !document.getElementById(eid)
9882             // display none elements have no offsetParent so we will
9883             // also try to look it up by it's id. However, check
9884             // offsetParent first so we don't do unneeded lookups.
9885             // This enables collection of elements that are not orphans
9886             // directly, but somewhere up the line they have an orphan
9887             // parent.
9888             // -------------------------------------------------------
9889             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9890                 delete El.cache[eid];
9891                 if(d && Roo.enableListenerCollection){
9892                     E.purgeElement(d);
9893                 }
9894             }
9895         }
9896     }
9897     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9898
9899
9900     // dom is optional
9901     El.Flyweight = function(dom){
9902         this.dom = dom;
9903     };
9904     El.Flyweight.prototype = El.prototype;
9905
9906     El._flyweights = {};
9907     /**
9908      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9909      * the dom node can be overwritten by other code.
9910      * @param {String/HTMLElement} el The dom node or id
9911      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9912      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9913      * @static
9914      * @return {Element} The shared Element object
9915      */
9916     El.fly = function(el, named){
9917         named = named || '_global';
9918         el = Roo.getDom(el);
9919         if(!el){
9920             return null;
9921         }
9922         if(!El._flyweights[named]){
9923             El._flyweights[named] = new El.Flyweight();
9924         }
9925         El._flyweights[named].dom = el;
9926         return El._flyweights[named];
9927     };
9928
9929     /**
9930      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9931      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9932      * Shorthand of {@link Roo.Element#get}
9933      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9934      * @return {Element} The Element object
9935      * @member Roo
9936      * @method get
9937      */
9938     Roo.get = El.get;
9939     /**
9940      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9941      * the dom node can be overwritten by other code.
9942      * Shorthand of {@link Roo.Element#fly}
9943      * @param {String/HTMLElement} el The dom node or id
9944      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9945      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9946      * @static
9947      * @return {Element} The shared Element object
9948      * @member Roo
9949      * @method fly
9950      */
9951     Roo.fly = El.fly;
9952
9953     // speedy lookup for elements never to box adjust
9954     var noBoxAdjust = Roo.isStrict ? {
9955         select:1
9956     } : {
9957         input:1, select:1, textarea:1
9958     };
9959     if(Roo.isIE || Roo.isGecko){
9960         noBoxAdjust['button'] = 1;
9961     }
9962
9963
9964     Roo.EventManager.on(window, 'unload', function(){
9965         delete El.cache;
9966         delete El._flyweights;
9967     });
9968 })();
9969
9970
9971
9972
9973 if(Roo.DomQuery){
9974     Roo.Element.selectorFunction = Roo.DomQuery.select;
9975 }
9976
9977 Roo.Element.select = function(selector, unique, root){
9978     var els;
9979     if(typeof selector == "string"){
9980         els = Roo.Element.selectorFunction(selector, root);
9981     }else if(selector.length !== undefined){
9982         els = selector;
9983     }else{
9984         throw "Invalid selector";
9985     }
9986     if(unique === true){
9987         return new Roo.CompositeElement(els);
9988     }else{
9989         return new Roo.CompositeElementLite(els);
9990     }
9991 };
9992 /**
9993  * Selects elements based on the passed CSS selector to enable working on them as 1.
9994  * @param {String/Array} selector The CSS selector or an array of elements
9995  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9996  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9997  * @return {CompositeElementLite/CompositeElement}
9998  * @member Roo
9999  * @method select
10000  */
10001 Roo.select = Roo.Element.select;
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016 /*
10017  * Based on:
10018  * Ext JS Library 1.1.1
10019  * Copyright(c) 2006-2007, Ext JS, LLC.
10020  *
10021  * Originally Released Under LGPL - original licence link has changed is not relivant.
10022  *
10023  * Fork - LGPL
10024  * <script type="text/javascript">
10025  */
10026
10027
10028
10029 //Notifies Element that fx methods are available
10030 Roo.enableFx = true;
10031
10032 /**
10033  * @class Roo.Fx
10034  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10035  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10036  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10037  * Element effects to work.</p><br/>
10038  *
10039  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10040  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10041  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10042  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10043  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10044  * expected results and should be done with care.</p><br/>
10045  *
10046  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10047  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10048 <pre>
10049 Value  Description
10050 -----  -----------------------------
10051 tl     The top left corner
10052 t      The center of the top edge
10053 tr     The top right corner
10054 l      The center of the left edge
10055 r      The center of the right edge
10056 bl     The bottom left corner
10057 b      The center of the bottom edge
10058 br     The bottom right corner
10059 </pre>
10060  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10061  * below are common options that can be passed to any Fx method.</b>
10062  * @cfg {Function} callback A function called when the effect is finished
10063  * @cfg {Object} scope The scope of the effect function
10064  * @cfg {String} easing A valid Easing value for the effect
10065  * @cfg {String} afterCls A css class to apply after the effect
10066  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10067  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10068  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10069  * effects that end with the element being visually hidden, ignored otherwise)
10070  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10071  * a function which returns such a specification that will be applied to the Element after the effect finishes
10072  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10073  * @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
10074  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10075  */
10076 Roo.Fx = {
10077         /**
10078          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10079          * origin for the slide effect.  This function automatically handles wrapping the element with
10080          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10081          * Usage:
10082          *<pre><code>
10083 // default: slide the element in from the top
10084 el.slideIn();
10085
10086 // custom: slide the element in from the right with a 2-second duration
10087 el.slideIn('r', { duration: 2 });
10088
10089 // common config options shown with default values
10090 el.slideIn('t', {
10091     easing: 'easeOut',
10092     duration: .5
10093 });
10094 </code></pre>
10095          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10096          * @param {Object} options (optional) Object literal with any of the Fx config options
10097          * @return {Roo.Element} The Element
10098          */
10099     slideIn : function(anchor, o){
10100         var el = this.getFxEl();
10101         o = o || {};
10102
10103         el.queueFx(o, function(){
10104
10105             anchor = anchor || "t";
10106
10107             // fix display to visibility
10108             this.fixDisplay();
10109
10110             // restore values after effect
10111             var r = this.getFxRestore();
10112             var b = this.getBox();
10113             // fixed size for slide
10114             this.setSize(b);
10115
10116             // wrap if needed
10117             var wrap = this.fxWrap(r.pos, o, "hidden");
10118
10119             var st = this.dom.style;
10120             st.visibility = "visible";
10121             st.position = "absolute";
10122
10123             // clear out temp styles after slide and unwrap
10124             var after = function(){
10125                 el.fxUnwrap(wrap, r.pos, o);
10126                 st.width = r.width;
10127                 st.height = r.height;
10128                 el.afterFx(o);
10129             };
10130             // time to calc the positions
10131             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10132
10133             switch(anchor.toLowerCase()){
10134                 case "t":
10135                     wrap.setSize(b.width, 0);
10136                     st.left = st.bottom = "0";
10137                     a = {height: bh};
10138                 break;
10139                 case "l":
10140                     wrap.setSize(0, b.height);
10141                     st.right = st.top = "0";
10142                     a = {width: bw};
10143                 break;
10144                 case "r":
10145                     wrap.setSize(0, b.height);
10146                     wrap.setX(b.right);
10147                     st.left = st.top = "0";
10148                     a = {width: bw, points: pt};
10149                 break;
10150                 case "b":
10151                     wrap.setSize(b.width, 0);
10152                     wrap.setY(b.bottom);
10153                     st.left = st.top = "0";
10154                     a = {height: bh, points: pt};
10155                 break;
10156                 case "tl":
10157                     wrap.setSize(0, 0);
10158                     st.right = st.bottom = "0";
10159                     a = {width: bw, height: bh};
10160                 break;
10161                 case "bl":
10162                     wrap.setSize(0, 0);
10163                     wrap.setY(b.y+b.height);
10164                     st.right = st.top = "0";
10165                     a = {width: bw, height: bh, points: pt};
10166                 break;
10167                 case "br":
10168                     wrap.setSize(0, 0);
10169                     wrap.setXY([b.right, b.bottom]);
10170                     st.left = st.top = "0";
10171                     a = {width: bw, height: bh, points: pt};
10172                 break;
10173                 case "tr":
10174                     wrap.setSize(0, 0);
10175                     wrap.setX(b.x+b.width);
10176                     st.left = st.bottom = "0";
10177                     a = {width: bw, height: bh, points: pt};
10178                 break;
10179             }
10180             this.dom.style.visibility = "visible";
10181             wrap.show();
10182
10183             arguments.callee.anim = wrap.fxanim(a,
10184                 o,
10185                 'motion',
10186                 .5,
10187                 'easeOut', after);
10188         });
10189         return this;
10190     },
10191     
10192         /**
10193          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10194          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10195          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10196          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10197          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10198          * Usage:
10199          *<pre><code>
10200 // default: slide the element out to the top
10201 el.slideOut();
10202
10203 // custom: slide the element out to the right with a 2-second duration
10204 el.slideOut('r', { duration: 2 });
10205
10206 // common config options shown with default values
10207 el.slideOut('t', {
10208     easing: 'easeOut',
10209     duration: .5,
10210     remove: false,
10211     useDisplay: false
10212 });
10213 </code></pre>
10214          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10215          * @param {Object} options (optional) Object literal with any of the Fx config options
10216          * @return {Roo.Element} The Element
10217          */
10218     slideOut : function(anchor, o){
10219         var el = this.getFxEl();
10220         o = o || {};
10221
10222         el.queueFx(o, function(){
10223
10224             anchor = anchor || "t";
10225
10226             // restore values after effect
10227             var r = this.getFxRestore();
10228             
10229             var b = this.getBox();
10230             // fixed size for slide
10231             this.setSize(b);
10232
10233             // wrap if needed
10234             var wrap = this.fxWrap(r.pos, o, "visible");
10235
10236             var st = this.dom.style;
10237             st.visibility = "visible";
10238             st.position = "absolute";
10239
10240             wrap.setSize(b);
10241
10242             var after = function(){
10243                 if(o.useDisplay){
10244                     el.setDisplayed(false);
10245                 }else{
10246                     el.hide();
10247                 }
10248
10249                 el.fxUnwrap(wrap, r.pos, o);
10250
10251                 st.width = r.width;
10252                 st.height = r.height;
10253
10254                 el.afterFx(o);
10255             };
10256
10257             var a, zero = {to: 0};
10258             switch(anchor.toLowerCase()){
10259                 case "t":
10260                     st.left = st.bottom = "0";
10261                     a = {height: zero};
10262                 break;
10263                 case "l":
10264                     st.right = st.top = "0";
10265                     a = {width: zero};
10266                 break;
10267                 case "r":
10268                     st.left = st.top = "0";
10269                     a = {width: zero, points: {to:[b.right, b.y]}};
10270                 break;
10271                 case "b":
10272                     st.left = st.top = "0";
10273                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10274                 break;
10275                 case "tl":
10276                     st.right = st.bottom = "0";
10277                     a = {width: zero, height: zero};
10278                 break;
10279                 case "bl":
10280                     st.right = st.top = "0";
10281                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10282                 break;
10283                 case "br":
10284                     st.left = st.top = "0";
10285                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10286                 break;
10287                 case "tr":
10288                     st.left = st.bottom = "0";
10289                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10290                 break;
10291             }
10292
10293             arguments.callee.anim = wrap.fxanim(a,
10294                 o,
10295                 'motion',
10296                 .5,
10297                 "easeOut", after);
10298         });
10299         return this;
10300     },
10301
10302         /**
10303          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10304          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10305          * The element must be removed from the DOM using the 'remove' config option if desired.
10306          * Usage:
10307          *<pre><code>
10308 // default
10309 el.puff();
10310
10311 // common config options shown with default values
10312 el.puff({
10313     easing: 'easeOut',
10314     duration: .5,
10315     remove: false,
10316     useDisplay: false
10317 });
10318 </code></pre>
10319          * @param {Object} options (optional) Object literal with any of the Fx config options
10320          * @return {Roo.Element} The Element
10321          */
10322     puff : function(o){
10323         var el = this.getFxEl();
10324         o = o || {};
10325
10326         el.queueFx(o, function(){
10327             this.clearOpacity();
10328             this.show();
10329
10330             // restore values after effect
10331             var r = this.getFxRestore();
10332             var st = this.dom.style;
10333
10334             var after = function(){
10335                 if(o.useDisplay){
10336                     el.setDisplayed(false);
10337                 }else{
10338                     el.hide();
10339                 }
10340
10341                 el.clearOpacity();
10342
10343                 el.setPositioning(r.pos);
10344                 st.width = r.width;
10345                 st.height = r.height;
10346                 st.fontSize = '';
10347                 el.afterFx(o);
10348             };
10349
10350             var width = this.getWidth();
10351             var height = this.getHeight();
10352
10353             arguments.callee.anim = this.fxanim({
10354                     width : {to: this.adjustWidth(width * 2)},
10355                     height : {to: this.adjustHeight(height * 2)},
10356                     points : {by: [-(width * .5), -(height * .5)]},
10357                     opacity : {to: 0},
10358                     fontSize: {to:200, unit: "%"}
10359                 },
10360                 o,
10361                 'motion',
10362                 .5,
10363                 "easeOut", after);
10364         });
10365         return this;
10366     },
10367
10368         /**
10369          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10370          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10371          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10372          * Usage:
10373          *<pre><code>
10374 // default
10375 el.switchOff();
10376
10377 // all config options shown with default values
10378 el.switchOff({
10379     easing: 'easeIn',
10380     duration: .3,
10381     remove: false,
10382     useDisplay: false
10383 });
10384 </code></pre>
10385          * @param {Object} options (optional) Object literal with any of the Fx config options
10386          * @return {Roo.Element} The Element
10387          */
10388     switchOff : function(o){
10389         var el = this.getFxEl();
10390         o = o || {};
10391
10392         el.queueFx(o, function(){
10393             this.clearOpacity();
10394             this.clip();
10395
10396             // restore values after effect
10397             var r = this.getFxRestore();
10398             var st = this.dom.style;
10399
10400             var after = function(){
10401                 if(o.useDisplay){
10402                     el.setDisplayed(false);
10403                 }else{
10404                     el.hide();
10405                 }
10406
10407                 el.clearOpacity();
10408                 el.setPositioning(r.pos);
10409                 st.width = r.width;
10410                 st.height = r.height;
10411
10412                 el.afterFx(o);
10413             };
10414
10415             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10416                 this.clearOpacity();
10417                 (function(){
10418                     this.fxanim({
10419                         height:{to:1},
10420                         points:{by:[0, this.getHeight() * .5]}
10421                     }, o, 'motion', 0.3, 'easeIn', after);
10422                 }).defer(100, this);
10423             });
10424         });
10425         return this;
10426     },
10427
10428     /**
10429      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10430      * changed using the "attr" config option) and then fading back to the original color. If no original
10431      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10432      * Usage:
10433 <pre><code>
10434 // default: highlight background to yellow
10435 el.highlight();
10436
10437 // custom: highlight foreground text to blue for 2 seconds
10438 el.highlight("0000ff", { attr: 'color', duration: 2 });
10439
10440 // common config options shown with default values
10441 el.highlight("ffff9c", {
10442     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10443     endColor: (current color) or "ffffff",
10444     easing: 'easeIn',
10445     duration: 1
10446 });
10447 </code></pre>
10448      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10449      * @param {Object} options (optional) Object literal with any of the Fx config options
10450      * @return {Roo.Element} The Element
10451      */ 
10452     highlight : function(color, o){
10453         var el = this.getFxEl();
10454         o = o || {};
10455
10456         el.queueFx(o, function(){
10457             color = color || "ffff9c";
10458             attr = o.attr || "backgroundColor";
10459
10460             this.clearOpacity();
10461             this.show();
10462
10463             var origColor = this.getColor(attr);
10464             var restoreColor = this.dom.style[attr];
10465             endColor = (o.endColor || origColor) || "ffffff";
10466
10467             var after = function(){
10468                 el.dom.style[attr] = restoreColor;
10469                 el.afterFx(o);
10470             };
10471
10472             var a = {};
10473             a[attr] = {from: color, to: endColor};
10474             arguments.callee.anim = this.fxanim(a,
10475                 o,
10476                 'color',
10477                 1,
10478                 'easeIn', after);
10479         });
10480         return this;
10481     },
10482
10483    /**
10484     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10485     * Usage:
10486 <pre><code>
10487 // default: a single light blue ripple
10488 el.frame();
10489
10490 // custom: 3 red ripples lasting 3 seconds total
10491 el.frame("ff0000", 3, { duration: 3 });
10492
10493 // common config options shown with default values
10494 el.frame("C3DAF9", 1, {
10495     duration: 1 //duration of entire animation (not each individual ripple)
10496     // Note: Easing is not configurable and will be ignored if included
10497 });
10498 </code></pre>
10499     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10500     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10501     * @param {Object} options (optional) Object literal with any of the Fx config options
10502     * @return {Roo.Element} The Element
10503     */
10504     frame : function(color, count, o){
10505         var el = this.getFxEl();
10506         o = o || {};
10507
10508         el.queueFx(o, function(){
10509             color = color || "#C3DAF9";
10510             if(color.length == 6){
10511                 color = "#" + color;
10512             }
10513             count = count || 1;
10514             duration = o.duration || 1;
10515             this.show();
10516
10517             var b = this.getBox();
10518             var animFn = function(){
10519                 var proxy = this.createProxy({
10520
10521                      style:{
10522                         visbility:"hidden",
10523                         position:"absolute",
10524                         "z-index":"35000", // yee haw
10525                         border:"0px solid " + color
10526                      }
10527                   });
10528                 var scale = Roo.isBorderBox ? 2 : 1;
10529                 proxy.animate({
10530                     top:{from:b.y, to:b.y - 20},
10531                     left:{from:b.x, to:b.x - 20},
10532                     borderWidth:{from:0, to:10},
10533                     opacity:{from:1, to:0},
10534                     height:{from:b.height, to:(b.height + (20*scale))},
10535                     width:{from:b.width, to:(b.width + (20*scale))}
10536                 }, duration, function(){
10537                     proxy.remove();
10538                 });
10539                 if(--count > 0){
10540                      animFn.defer((duration/2)*1000, this);
10541                 }else{
10542                     el.afterFx(o);
10543                 }
10544             };
10545             animFn.call(this);
10546         });
10547         return this;
10548     },
10549
10550    /**
10551     * Creates a pause before any subsequent queued effects begin.  If there are
10552     * no effects queued after the pause it will have no effect.
10553     * Usage:
10554 <pre><code>
10555 el.pause(1);
10556 </code></pre>
10557     * @param {Number} seconds The length of time to pause (in seconds)
10558     * @return {Roo.Element} The Element
10559     */
10560     pause : function(seconds){
10561         var el = this.getFxEl();
10562         var o = {};
10563
10564         el.queueFx(o, function(){
10565             setTimeout(function(){
10566                 el.afterFx(o);
10567             }, seconds * 1000);
10568         });
10569         return this;
10570     },
10571
10572    /**
10573     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10574     * using the "endOpacity" config option.
10575     * Usage:
10576 <pre><code>
10577 // default: fade in from opacity 0 to 100%
10578 el.fadeIn();
10579
10580 // custom: fade in from opacity 0 to 75% over 2 seconds
10581 el.fadeIn({ endOpacity: .75, duration: 2});
10582
10583 // common config options shown with default values
10584 el.fadeIn({
10585     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10586     easing: 'easeOut',
10587     duration: .5
10588 });
10589 </code></pre>
10590     * @param {Object} options (optional) Object literal with any of the Fx config options
10591     * @return {Roo.Element} The Element
10592     */
10593     fadeIn : function(o){
10594         var el = this.getFxEl();
10595         o = o || {};
10596         el.queueFx(o, function(){
10597             this.setOpacity(0);
10598             this.fixDisplay();
10599             this.dom.style.visibility = 'visible';
10600             var to = o.endOpacity || 1;
10601             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10602                 o, null, .5, "easeOut", function(){
10603                 if(to == 1){
10604                     this.clearOpacity();
10605                 }
10606                 el.afterFx(o);
10607             });
10608         });
10609         return this;
10610     },
10611
10612    /**
10613     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10614     * using the "endOpacity" config option.
10615     * Usage:
10616 <pre><code>
10617 // default: fade out from the element's current opacity to 0
10618 el.fadeOut();
10619
10620 // custom: fade out from the element's current opacity to 25% over 2 seconds
10621 el.fadeOut({ endOpacity: .25, duration: 2});
10622
10623 // common config options shown with default values
10624 el.fadeOut({
10625     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10626     easing: 'easeOut',
10627     duration: .5
10628     remove: false,
10629     useDisplay: false
10630 });
10631 </code></pre>
10632     * @param {Object} options (optional) Object literal with any of the Fx config options
10633     * @return {Roo.Element} The Element
10634     */
10635     fadeOut : function(o){
10636         var el = this.getFxEl();
10637         o = o || {};
10638         el.queueFx(o, function(){
10639             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10640                 o, null, .5, "easeOut", function(){
10641                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10642                      this.dom.style.display = "none";
10643                 }else{
10644                      this.dom.style.visibility = "hidden";
10645                 }
10646                 this.clearOpacity();
10647                 el.afterFx(o);
10648             });
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Animates the transition of an element's dimensions from a starting height/width
10655     * to an ending height/width.
10656     * Usage:
10657 <pre><code>
10658 // change height and width to 100x100 pixels
10659 el.scale(100, 100);
10660
10661 // common config options shown with default values.  The height and width will default to
10662 // the element's existing values if passed as null.
10663 el.scale(
10664     [element's width],
10665     [element's height], {
10666     easing: 'easeOut',
10667     duration: .35
10668 });
10669 </code></pre>
10670     * @param {Number} width  The new width (pass undefined to keep the original width)
10671     * @param {Number} height  The new height (pass undefined to keep the original height)
10672     * @param {Object} options (optional) Object literal with any of the Fx config options
10673     * @return {Roo.Element} The Element
10674     */
10675     scale : function(w, h, o){
10676         this.shift(Roo.apply({}, o, {
10677             width: w,
10678             height: h
10679         }));
10680         return this;
10681     },
10682
10683    /**
10684     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10685     * Any of these properties not specified in the config object will not be changed.  This effect 
10686     * requires that at least one new dimension, position or opacity setting must be passed in on
10687     * the config object in order for the function to have any effect.
10688     * Usage:
10689 <pre><code>
10690 // slide the element horizontally to x position 200 while changing the height and opacity
10691 el.shift({ x: 200, height: 50, opacity: .8 });
10692
10693 // common config options shown with default values.
10694 el.shift({
10695     width: [element's width],
10696     height: [element's height],
10697     x: [element's x position],
10698     y: [element's y position],
10699     opacity: [element's opacity],
10700     easing: 'easeOut',
10701     duration: .35
10702 });
10703 </code></pre>
10704     * @param {Object} options  Object literal with any of the Fx config options
10705     * @return {Roo.Element} The Element
10706     */
10707     shift : function(o){
10708         var el = this.getFxEl();
10709         o = o || {};
10710         el.queueFx(o, function(){
10711             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10712             if(w !== undefined){
10713                 a.width = {to: this.adjustWidth(w)};
10714             }
10715             if(h !== undefined){
10716                 a.height = {to: this.adjustHeight(h)};
10717             }
10718             if(x !== undefined || y !== undefined){
10719                 a.points = {to: [
10720                     x !== undefined ? x : this.getX(),
10721                     y !== undefined ? y : this.getY()
10722                 ]};
10723             }
10724             if(op !== undefined){
10725                 a.opacity = {to: op};
10726             }
10727             if(o.xy !== undefined){
10728                 a.points = {to: o.xy};
10729             }
10730             arguments.callee.anim = this.fxanim(a,
10731                 o, 'motion', .35, "easeOut", function(){
10732                 el.afterFx(o);
10733             });
10734         });
10735         return this;
10736     },
10737
10738         /**
10739          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10740          * ending point of the effect.
10741          * Usage:
10742          *<pre><code>
10743 // default: slide the element downward while fading out
10744 el.ghost();
10745
10746 // custom: slide the element out to the right with a 2-second duration
10747 el.ghost('r', { duration: 2 });
10748
10749 // common config options shown with default values
10750 el.ghost('b', {
10751     easing: 'easeOut',
10752     duration: .5
10753     remove: false,
10754     useDisplay: false
10755 });
10756 </code></pre>
10757          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10758          * @param {Object} options (optional) Object literal with any of the Fx config options
10759          * @return {Roo.Element} The Element
10760          */
10761     ghost : function(anchor, o){
10762         var el = this.getFxEl();
10763         o = o || {};
10764
10765         el.queueFx(o, function(){
10766             anchor = anchor || "b";
10767
10768             // restore values after effect
10769             var r = this.getFxRestore();
10770             var w = this.getWidth(),
10771                 h = this.getHeight();
10772
10773             var st = this.dom.style;
10774
10775             var after = function(){
10776                 if(o.useDisplay){
10777                     el.setDisplayed(false);
10778                 }else{
10779                     el.hide();
10780                 }
10781
10782                 el.clearOpacity();
10783                 el.setPositioning(r.pos);
10784                 st.width = r.width;
10785                 st.height = r.height;
10786
10787                 el.afterFx(o);
10788             };
10789
10790             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10791             switch(anchor.toLowerCase()){
10792                 case "t":
10793                     pt.by = [0, -h];
10794                 break;
10795                 case "l":
10796                     pt.by = [-w, 0];
10797                 break;
10798                 case "r":
10799                     pt.by = [w, 0];
10800                 break;
10801                 case "b":
10802                     pt.by = [0, h];
10803                 break;
10804                 case "tl":
10805                     pt.by = [-w, -h];
10806                 break;
10807                 case "bl":
10808                     pt.by = [-w, h];
10809                 break;
10810                 case "br":
10811                     pt.by = [w, h];
10812                 break;
10813                 case "tr":
10814                     pt.by = [w, -h];
10815                 break;
10816             }
10817
10818             arguments.callee.anim = this.fxanim(a,
10819                 o,
10820                 'motion',
10821                 .5,
10822                 "easeOut", after);
10823         });
10824         return this;
10825     },
10826
10827         /**
10828          * Ensures that all effects queued after syncFx is called on the element are
10829          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10830          * @return {Roo.Element} The Element
10831          */
10832     syncFx : function(){
10833         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10834             block : false,
10835             concurrent : true,
10836             stopFx : false
10837         });
10838         return this;
10839     },
10840
10841         /**
10842          * Ensures that all effects queued after sequenceFx is called on the element are
10843          * run in sequence.  This is the opposite of {@link #syncFx}.
10844          * @return {Roo.Element} The Element
10845          */
10846     sequenceFx : function(){
10847         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10848             block : false,
10849             concurrent : false,
10850             stopFx : false
10851         });
10852         return this;
10853     },
10854
10855         /* @private */
10856     nextFx : function(){
10857         var ef = this.fxQueue[0];
10858         if(ef){
10859             ef.call(this);
10860         }
10861     },
10862
10863         /**
10864          * Returns true if the element has any effects actively running or queued, else returns false.
10865          * @return {Boolean} True if element has active effects, else false
10866          */
10867     hasActiveFx : function(){
10868         return this.fxQueue && this.fxQueue[0];
10869     },
10870
10871         /**
10872          * Stops any running effects and clears the element's internal effects queue if it contains
10873          * any additional effects that haven't started yet.
10874          * @return {Roo.Element} The Element
10875          */
10876     stopFx : function(){
10877         if(this.hasActiveFx()){
10878             var cur = this.fxQueue[0];
10879             if(cur && cur.anim && cur.anim.isAnimated()){
10880                 this.fxQueue = [cur]; // clear out others
10881                 cur.anim.stop(true);
10882             }
10883         }
10884         return this;
10885     },
10886
10887         /* @private */
10888     beforeFx : function(o){
10889         if(this.hasActiveFx() && !o.concurrent){
10890            if(o.stopFx){
10891                this.stopFx();
10892                return true;
10893            }
10894            return false;
10895         }
10896         return true;
10897     },
10898
10899         /**
10900          * Returns true if the element is currently blocking so that no other effect can be queued
10901          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10902          * used to ensure that an effect initiated by a user action runs to completion prior to the
10903          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10904          * @return {Boolean} True if blocking, else false
10905          */
10906     hasFxBlock : function(){
10907         var q = this.fxQueue;
10908         return q && q[0] && q[0].block;
10909     },
10910
10911         /* @private */
10912     queueFx : function(o, fn){
10913         if(!this.fxQueue){
10914             this.fxQueue = [];
10915         }
10916         if(!this.hasFxBlock()){
10917             Roo.applyIf(o, this.fxDefaults);
10918             if(!o.concurrent){
10919                 var run = this.beforeFx(o);
10920                 fn.block = o.block;
10921                 this.fxQueue.push(fn);
10922                 if(run){
10923                     this.nextFx();
10924                 }
10925             }else{
10926                 fn.call(this);
10927             }
10928         }
10929         return this;
10930     },
10931
10932         /* @private */
10933     fxWrap : function(pos, o, vis){
10934         var wrap;
10935         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10936             var wrapXY;
10937             if(o.fixPosition){
10938                 wrapXY = this.getXY();
10939             }
10940             var div = document.createElement("div");
10941             div.style.visibility = vis;
10942             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10943             wrap.setPositioning(pos);
10944             if(wrap.getStyle("position") == "static"){
10945                 wrap.position("relative");
10946             }
10947             this.clearPositioning('auto');
10948             wrap.clip();
10949             wrap.dom.appendChild(this.dom);
10950             if(wrapXY){
10951                 wrap.setXY(wrapXY);
10952             }
10953         }
10954         return wrap;
10955     },
10956
10957         /* @private */
10958     fxUnwrap : function(wrap, pos, o){
10959         this.clearPositioning();
10960         this.setPositioning(pos);
10961         if(!o.wrap){
10962             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10963             wrap.remove();
10964         }
10965     },
10966
10967         /* @private */
10968     getFxRestore : function(){
10969         var st = this.dom.style;
10970         return {pos: this.getPositioning(), width: st.width, height : st.height};
10971     },
10972
10973         /* @private */
10974     afterFx : function(o){
10975         if(o.afterStyle){
10976             this.applyStyles(o.afterStyle);
10977         }
10978         if(o.afterCls){
10979             this.addClass(o.afterCls);
10980         }
10981         if(o.remove === true){
10982             this.remove();
10983         }
10984         Roo.callback(o.callback, o.scope, [this]);
10985         if(!o.concurrent){
10986             this.fxQueue.shift();
10987             this.nextFx();
10988         }
10989     },
10990
10991         /* @private */
10992     getFxEl : function(){ // support for composite element fx
10993         return Roo.get(this.dom);
10994     },
10995
10996         /* @private */
10997     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10998         animType = animType || 'run';
10999         opt = opt || {};
11000         var anim = Roo.lib.Anim[animType](
11001             this.dom, args,
11002             (opt.duration || defaultDur) || .35,
11003             (opt.easing || defaultEase) || 'easeOut',
11004             function(){
11005                 Roo.callback(cb, this);
11006             },
11007             this
11008         );
11009         opt.anim = anim;
11010         return anim;
11011     }
11012 };
11013
11014 // backwords compat
11015 Roo.Fx.resize = Roo.Fx.scale;
11016
11017 //When included, Roo.Fx is automatically applied to Element so that all basic
11018 //effects are available directly via the Element API
11019 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11020  * Based on:
11021  * Ext JS Library 1.1.1
11022  * Copyright(c) 2006-2007, Ext JS, LLC.
11023  *
11024  * Originally Released Under LGPL - original licence link has changed is not relivant.
11025  *
11026  * Fork - LGPL
11027  * <script type="text/javascript">
11028  */
11029
11030
11031 /**
11032  * @class Roo.CompositeElement
11033  * Standard composite class. Creates a Roo.Element for every element in the collection.
11034  * <br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  * <br><br>
11038  * All methods return <i>this</i> and can be chained.
11039  <pre><code>
11040  var els = Roo.select("#some-el div.some-class", true);
11041  // or select directly from an existing element
11042  var el = Roo.get('some-el');
11043  el.select('div.some-class', true);
11044
11045  els.setWidth(100); // all elements become 100 width
11046  els.hide(true); // all elements fade out and hide
11047  // or
11048  els.setWidth(100).hide(true);
11049  </code></pre>
11050  */
11051 Roo.CompositeElement = function(els){
11052     this.elements = [];
11053     this.addElements(els);
11054 };
11055 Roo.CompositeElement.prototype = {
11056     isComposite: true,
11057     addElements : function(els){
11058         if(!els) {
11059             return this;
11060         }
11061         if(typeof els == "string"){
11062             els = Roo.Element.selectorFunction(els);
11063         }
11064         var yels = this.elements;
11065         var index = yels.length-1;
11066         for(var i = 0, len = els.length; i < len; i++) {
11067                 yels[++index] = Roo.get(els[i]);
11068         }
11069         return this;
11070     },
11071
11072     /**
11073     * Clears this composite and adds the elements returned by the passed selector.
11074     * @param {String/Array} els A string CSS selector, an array of elements or an element
11075     * @return {CompositeElement} this
11076     */
11077     fill : function(els){
11078         this.elements = [];
11079         this.add(els);
11080         return this;
11081     },
11082
11083     /**
11084     * Filters this composite to only elements that match the passed selector.
11085     * @param {String} selector A string CSS selector
11086     * @param {Boolean} inverse return inverse filter (not matches)
11087     * @return {CompositeElement} this
11088     */
11089     filter : function(selector, inverse){
11090         var els = [];
11091         inverse = inverse || false;
11092         this.each(function(el){
11093             var match = inverse ? !el.is(selector) : el.is(selector);
11094             if(match){
11095                 els[els.length] = el.dom;
11096             }
11097         });
11098         this.fill(els);
11099         return this;
11100     },
11101
11102     invoke : function(fn, args){
11103         var els = this.elements;
11104         for(var i = 0, len = els.length; i < len; i++) {
11105                 Roo.Element.prototype[fn].apply(els[i], args);
11106         }
11107         return this;
11108     },
11109     /**
11110     * Adds elements to this composite.
11111     * @param {String/Array} els A string CSS selector, an array of elements or an element
11112     * @return {CompositeElement} this
11113     */
11114     add : function(els){
11115         if(typeof els == "string"){
11116             this.addElements(Roo.Element.selectorFunction(els));
11117         }else if(els.length !== undefined){
11118             this.addElements(els);
11119         }else{
11120             this.addElements([els]);
11121         }
11122         return this;
11123     },
11124     /**
11125     * Calls the passed function passing (el, this, index) for each element in this composite.
11126     * @param {Function} fn The function to call
11127     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11128     * @return {CompositeElement} this
11129     */
11130     each : function(fn, scope){
11131         var els = this.elements;
11132         for(var i = 0, len = els.length; i < len; i++){
11133             if(fn.call(scope || els[i], els[i], this, i) === false) {
11134                 break;
11135             }
11136         }
11137         return this;
11138     },
11139
11140     /**
11141      * Returns the Element object at the specified index
11142      * @param {Number} index
11143      * @return {Roo.Element}
11144      */
11145     item : function(index){
11146         return this.elements[index] || null;
11147     },
11148
11149     /**
11150      * Returns the first Element
11151      * @return {Roo.Element}
11152      */
11153     first : function(){
11154         return this.item(0);
11155     },
11156
11157     /**
11158      * Returns the last Element
11159      * @return {Roo.Element}
11160      */
11161     last : function(){
11162         return this.item(this.elements.length-1);
11163     },
11164
11165     /**
11166      * Returns the number of elements in this composite
11167      * @return Number
11168      */
11169     getCount : function(){
11170         return this.elements.length;
11171     },
11172
11173     /**
11174      * Returns true if this composite contains the passed element
11175      * @return Boolean
11176      */
11177     contains : function(el){
11178         return this.indexOf(el) !== -1;
11179     },
11180
11181     /**
11182      * Returns true if this composite contains the passed element
11183      * @return Boolean
11184      */
11185     indexOf : function(el){
11186         return this.elements.indexOf(Roo.get(el));
11187     },
11188
11189
11190     /**
11191     * Removes the specified element(s).
11192     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11193     * or an array of any of those.
11194     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11195     * @return {CompositeElement} this
11196     */
11197     removeElement : function(el, removeDom){
11198         if(el instanceof Array){
11199             for(var i = 0, len = el.length; i < len; i++){
11200                 this.removeElement(el[i]);
11201             }
11202             return this;
11203         }
11204         var index = typeof el == 'number' ? el : this.indexOf(el);
11205         if(index !== -1){
11206             if(removeDom){
11207                 var d = this.elements[index];
11208                 if(d.dom){
11209                     d.remove();
11210                 }else{
11211                     d.parentNode.removeChild(d);
11212                 }
11213             }
11214             this.elements.splice(index, 1);
11215         }
11216         return this;
11217     },
11218
11219     /**
11220     * Replaces the specified element with the passed element.
11221     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11222     * to replace.
11223     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11224     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11225     * @return {CompositeElement} this
11226     */
11227     replaceElement : function(el, replacement, domReplace){
11228         var index = typeof el == 'number' ? el : this.indexOf(el);
11229         if(index !== -1){
11230             if(domReplace){
11231                 this.elements[index].replaceWith(replacement);
11232             }else{
11233                 this.elements.splice(index, 1, Roo.get(replacement))
11234             }
11235         }
11236         return this;
11237     },
11238
11239     /**
11240      * Removes all elements.
11241      */
11242     clear : function(){
11243         this.elements = [];
11244     }
11245 };
11246 (function(){
11247     Roo.CompositeElement.createCall = function(proto, fnName){
11248         if(!proto[fnName]){
11249             proto[fnName] = function(){
11250                 return this.invoke(fnName, arguments);
11251             };
11252         }
11253     };
11254     for(var fnName in Roo.Element.prototype){
11255         if(typeof Roo.Element.prototype[fnName] == "function"){
11256             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11257         }
11258     };
11259 })();
11260 /*
11261  * Based on:
11262  * Ext JS Library 1.1.1
11263  * Copyright(c) 2006-2007, Ext JS, LLC.
11264  *
11265  * Originally Released Under LGPL - original licence link has changed is not relivant.
11266  *
11267  * Fork - LGPL
11268  * <script type="text/javascript">
11269  */
11270
11271 /**
11272  * @class Roo.CompositeElementLite
11273  * @extends Roo.CompositeElement
11274  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11275  <pre><code>
11276  var els = Roo.select("#some-el div.some-class");
11277  // or select directly from an existing element
11278  var el = Roo.get('some-el');
11279  el.select('div.some-class');
11280
11281  els.setWidth(100); // all elements become 100 width
11282  els.hide(true); // all elements fade out and hide
11283  // or
11284  els.setWidth(100).hide(true);
11285  </code></pre><br><br>
11286  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11287  * actions will be performed on all the elements in this collection.</b>
11288  */
11289 Roo.CompositeElementLite = function(els){
11290     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11291     this.el = new Roo.Element.Flyweight();
11292 };
11293 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11294     addElements : function(els){
11295         if(els){
11296             if(els instanceof Array){
11297                 this.elements = this.elements.concat(els);
11298             }else{
11299                 var yels = this.elements;
11300                 var index = yels.length-1;
11301                 for(var i = 0, len = els.length; i < len; i++) {
11302                     yels[++index] = els[i];
11303                 }
11304             }
11305         }
11306         return this;
11307     },
11308     invoke : function(fn, args){
11309         var els = this.elements;
11310         var el = this.el;
11311         for(var i = 0, len = els.length; i < len; i++) {
11312             el.dom = els[i];
11313                 Roo.Element.prototype[fn].apply(el, args);
11314         }
11315         return this;
11316     },
11317     /**
11318      * Returns a flyweight Element of the dom element object at the specified index
11319      * @param {Number} index
11320      * @return {Roo.Element}
11321      */
11322     item : function(index){
11323         if(!this.elements[index]){
11324             return null;
11325         }
11326         this.el.dom = this.elements[index];
11327         return this.el;
11328     },
11329
11330     // fixes scope with flyweight
11331     addListener : function(eventName, handler, scope, opt){
11332         var els = this.elements;
11333         for(var i = 0, len = els.length; i < len; i++) {
11334             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11335         }
11336         return this;
11337     },
11338
11339     /**
11340     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11341     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11342     * a reference to the dom node, use el.dom.</b>
11343     * @param {Function} fn The function to call
11344     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11345     * @return {CompositeElement} this
11346     */
11347     each : function(fn, scope){
11348         var els = this.elements;
11349         var el = this.el;
11350         for(var i = 0, len = els.length; i < len; i++){
11351             el.dom = els[i];
11352                 if(fn.call(scope || el, el, this, i) === false){
11353                 break;
11354             }
11355         }
11356         return this;
11357     },
11358
11359     indexOf : function(el){
11360         return this.elements.indexOf(Roo.getDom(el));
11361     },
11362
11363     replaceElement : function(el, replacement, domReplace){
11364         var index = typeof el == 'number' ? el : this.indexOf(el);
11365         if(index !== -1){
11366             replacement = Roo.getDom(replacement);
11367             if(domReplace){
11368                 var d = this.elements[index];
11369                 d.parentNode.insertBefore(replacement, d);
11370                 d.parentNode.removeChild(d);
11371             }
11372             this.elements.splice(index, 1, replacement);
11373         }
11374         return this;
11375     }
11376 });
11377 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11378
11379 /*
11380  * Based on:
11381  * Ext JS Library 1.1.1
11382  * Copyright(c) 2006-2007, Ext JS, LLC.
11383  *
11384  * Originally Released Under LGPL - original licence link has changed is not relivant.
11385  *
11386  * Fork - LGPL
11387  * <script type="text/javascript">
11388  */
11389
11390  
11391
11392 /**
11393  * @class Roo.data.Connection
11394  * @extends Roo.util.Observable
11395  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11396  * either to a configured URL, or to a URL specified at request time.<br><br>
11397  * <p>
11398  * Requests made by this class are asynchronous, and will return immediately. No data from
11399  * the server will be available to the statement immediately following the {@link #request} call.
11400  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11401  * <p>
11402  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11403  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11404  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11405  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11406  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11407  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11408  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11409  * standard DOM methods.
11410  * @constructor
11411  * @param {Object} config a configuration object.
11412  */
11413 Roo.data.Connection = function(config){
11414     Roo.apply(this, config);
11415     this.addEvents({
11416         /**
11417          * @event beforerequest
11418          * Fires before a network request is made to retrieve a data object.
11419          * @param {Connection} conn This Connection object.
11420          * @param {Object} options The options config object passed to the {@link #request} method.
11421          */
11422         "beforerequest" : true,
11423         /**
11424          * @event requestcomplete
11425          * Fires if the request was successfully completed.
11426          * @param {Connection} conn This Connection object.
11427          * @param {Object} response The XHR object containing the response data.
11428          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11429          * @param {Object} options The options config object passed to the {@link #request} method.
11430          */
11431         "requestcomplete" : true,
11432         /**
11433          * @event requestexception
11434          * Fires if an error HTTP status was returned from the server.
11435          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11436          * @param {Connection} conn This Connection object.
11437          * @param {Object} response The XHR object containing the response data.
11438          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11439          * @param {Object} options The options config object passed to the {@link #request} method.
11440          */
11441         "requestexception" : true
11442     });
11443     Roo.data.Connection.superclass.constructor.call(this);
11444 };
11445
11446 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11447     /**
11448      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11449      */
11450     /**
11451      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11452      * extra parameters to each request made by this object. (defaults to undefined)
11453      */
11454     /**
11455      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11456      *  to each request made by this object. (defaults to undefined)
11457      */
11458     /**
11459      * @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)
11460      */
11461     /**
11462      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11463      */
11464     timeout : 30000,
11465     /**
11466      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11467      * @type Boolean
11468      */
11469     autoAbort:false,
11470
11471     /**
11472      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11473      * @type Boolean
11474      */
11475     disableCaching: true,
11476
11477     /**
11478      * Sends an HTTP request to a remote server.
11479      * @param {Object} options An object which may contain the following properties:<ul>
11480      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11481      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11482      * request, a url encoded string or a function to call to get either.</li>
11483      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11484      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11485      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11486      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11487      * <li>options {Object} The parameter to the request call.</li>
11488      * <li>success {Boolean} True if the request succeeded.</li>
11489      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11490      * </ul></li>
11491      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11492      * The callback is passed the following parameters:<ul>
11493      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11494      * <li>options {Object} The parameter to the request call.</li>
11495      * </ul></li>
11496      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11497      * The callback is passed the following parameters:<ul>
11498      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11499      * <li>options {Object} The parameter to the request call.</li>
11500      * </ul></li>
11501      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11502      * for the callback function. Defaults to the browser window.</li>
11503      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11504      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11505      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11506      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11507      * params for the post data. Any params will be appended to the URL.</li>
11508      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11509      * </ul>
11510      * @return {Number} transactionId
11511      */
11512     request : function(o){
11513         if(this.fireEvent("beforerequest", this, o) !== false){
11514             var p = o.params;
11515
11516             if(typeof p == "function"){
11517                 p = p.call(o.scope||window, o);
11518             }
11519             if(typeof p == "object"){
11520                 p = Roo.urlEncode(o.params);
11521             }
11522             if(this.extraParams){
11523                 var extras = Roo.urlEncode(this.extraParams);
11524                 p = p ? (p + '&' + extras) : extras;
11525             }
11526
11527             var url = o.url || this.url;
11528             if(typeof url == 'function'){
11529                 url = url.call(o.scope||window, o);
11530             }
11531
11532             if(o.form){
11533                 var form = Roo.getDom(o.form);
11534                 url = url || form.action;
11535
11536                 var enctype = form.getAttribute("enctype");
11537                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11538                     return this.doFormUpload(o, p, url);
11539                 }
11540                 var f = Roo.lib.Ajax.serializeForm(form);
11541                 p = p ? (p + '&' + f) : f;
11542             }
11543
11544             var hs = o.headers;
11545             if(this.defaultHeaders){
11546                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11547                 if(!o.headers){
11548                     o.headers = hs;
11549                 }
11550             }
11551
11552             var cb = {
11553                 success: this.handleResponse,
11554                 failure: this.handleFailure,
11555                 scope: this,
11556                 argument: {options: o},
11557                 timeout : o.timeout || this.timeout
11558             };
11559
11560             var method = o.method||this.method||(p ? "POST" : "GET");
11561
11562             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11563                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11564             }
11565
11566             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11567                 if(o.autoAbort){
11568                     this.abort();
11569                 }
11570             }else if(this.autoAbort !== false){
11571                 this.abort();
11572             }
11573
11574             if((method == 'GET' && p) || o.xmlData){
11575                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11576                 p = '';
11577             }
11578             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11579             return this.transId;
11580         }else{
11581             Roo.callback(o.callback, o.scope, [o, null, null]);
11582             return null;
11583         }
11584     },
11585
11586     /**
11587      * Determine whether this object has a request outstanding.
11588      * @param {Number} transactionId (Optional) defaults to the last transaction
11589      * @return {Boolean} True if there is an outstanding request.
11590      */
11591     isLoading : function(transId){
11592         if(transId){
11593             return Roo.lib.Ajax.isCallInProgress(transId);
11594         }else{
11595             return this.transId ? true : false;
11596         }
11597     },
11598
11599     /**
11600      * Aborts any outstanding request.
11601      * @param {Number} transactionId (Optional) defaults to the last transaction
11602      */
11603     abort : function(transId){
11604         if(transId || this.isLoading()){
11605             Roo.lib.Ajax.abort(transId || this.transId);
11606         }
11607     },
11608
11609     // private
11610     handleResponse : function(response){
11611         this.transId = false;
11612         var options = response.argument.options;
11613         response.argument = options ? options.argument : null;
11614         this.fireEvent("requestcomplete", this, response, options);
11615         Roo.callback(options.success, options.scope, [response, options]);
11616         Roo.callback(options.callback, options.scope, [options, true, response]);
11617     },
11618
11619     // private
11620     handleFailure : function(response, e){
11621         this.transId = false;
11622         var options = response.argument.options;
11623         response.argument = options ? options.argument : null;
11624         this.fireEvent("requestexception", this, response, options, e);
11625         Roo.callback(options.failure, options.scope, [response, options]);
11626         Roo.callback(options.callback, options.scope, [options, false, response]);
11627     },
11628
11629     // private
11630     doFormUpload : function(o, ps, url){
11631         var id = Roo.id();
11632         var frame = document.createElement('iframe');
11633         frame.id = id;
11634         frame.name = id;
11635         frame.className = 'x-hidden';
11636         if(Roo.isIE){
11637             frame.src = Roo.SSL_SECURE_URL;
11638         }
11639         document.body.appendChild(frame);
11640
11641         if(Roo.isIE){
11642            document.frames[id].name = id;
11643         }
11644
11645         var form = Roo.getDom(o.form);
11646         form.target = id;
11647         form.method = 'POST';
11648         form.enctype = form.encoding = 'multipart/form-data';
11649         if(url){
11650             form.action = url;
11651         }
11652
11653         var hiddens, hd;
11654         if(ps){ // add dynamic params
11655             hiddens = [];
11656             ps = Roo.urlDecode(ps, false);
11657             for(var k in ps){
11658                 if(ps.hasOwnProperty(k)){
11659                     hd = document.createElement('input');
11660                     hd.type = 'hidden';
11661                     hd.name = k;
11662                     hd.value = ps[k];
11663                     form.appendChild(hd);
11664                     hiddens.push(hd);
11665                 }
11666             }
11667         }
11668
11669         function cb(){
11670             var r = {  // bogus response object
11671                 responseText : '',
11672                 responseXML : null
11673             };
11674
11675             r.argument = o ? o.argument : null;
11676
11677             try { //
11678                 var doc;
11679                 if(Roo.isIE){
11680                     doc = frame.contentWindow.document;
11681                 }else {
11682                     doc = (frame.contentDocument || window.frames[id].document);
11683                 }
11684                 if(doc && doc.body){
11685                     r.responseText = doc.body.innerHTML;
11686                 }
11687                 if(doc && doc.XMLDocument){
11688                     r.responseXML = doc.XMLDocument;
11689                 }else {
11690                     r.responseXML = doc;
11691                 }
11692             }
11693             catch(e) {
11694                 // ignore
11695             }
11696
11697             Roo.EventManager.removeListener(frame, 'load', cb, this);
11698
11699             this.fireEvent("requestcomplete", this, r, o);
11700             Roo.callback(o.success, o.scope, [r, o]);
11701             Roo.callback(o.callback, o.scope, [o, true, r]);
11702
11703             setTimeout(function(){document.body.removeChild(frame);}, 100);
11704         }
11705
11706         Roo.EventManager.on(frame, 'load', cb, this);
11707         form.submit();
11708
11709         if(hiddens){ // remove dynamic params
11710             for(var i = 0, len = hiddens.length; i < len; i++){
11711                 form.removeChild(hiddens[i]);
11712             }
11713         }
11714     }
11715 });
11716 /*
11717  * Based on:
11718  * Ext JS Library 1.1.1
11719  * Copyright(c) 2006-2007, Ext JS, LLC.
11720  *
11721  * Originally Released Under LGPL - original licence link has changed is not relivant.
11722  *
11723  * Fork - LGPL
11724  * <script type="text/javascript">
11725  */
11726  
11727 /**
11728  * Global Ajax request class.
11729  * 
11730  * @class Roo.Ajax
11731  * @extends Roo.data.Connection
11732  * @static
11733  * 
11734  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11735  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11736  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11737  * @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)
11738  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11739  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11740  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11741  */
11742 Roo.Ajax = new Roo.data.Connection({
11743     // fix up the docs
11744     /**
11745      * @scope Roo.Ajax
11746      * @type {Boolear} 
11747      */
11748     autoAbort : false,
11749
11750     /**
11751      * Serialize the passed form into a url encoded string
11752      * @scope Roo.Ajax
11753      * @param {String/HTMLElement} form
11754      * @return {String}
11755      */
11756     serializeForm : function(form){
11757         return Roo.lib.Ajax.serializeForm(form);
11758     }
11759 });/*
11760  * Based on:
11761  * Ext JS Library 1.1.1
11762  * Copyright(c) 2006-2007, Ext JS, LLC.
11763  *
11764  * Originally Released Under LGPL - original licence link has changed is not relivant.
11765  *
11766  * Fork - LGPL
11767  * <script type="text/javascript">
11768  */
11769
11770  
11771 /**
11772  * @class Roo.UpdateManager
11773  * @extends Roo.util.Observable
11774  * Provides AJAX-style update for Element object.<br><br>
11775  * Usage:<br>
11776  * <pre><code>
11777  * // Get it from a Roo.Element object
11778  * var el = Roo.get("foo");
11779  * var mgr = el.getUpdateManager();
11780  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11781  * ...
11782  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11783  * <br>
11784  * // or directly (returns the same UpdateManager instance)
11785  * var mgr = new Roo.UpdateManager("myElementId");
11786  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11787  * mgr.on("update", myFcnNeedsToKnow);
11788  * <br>
11789    // short handed call directly from the element object
11790    Roo.get("foo").load({
11791         url: "bar.php",
11792         scripts:true,
11793         params: "for=bar",
11794         text: "Loading Foo..."
11795    });
11796  * </code></pre>
11797  * @constructor
11798  * Create new UpdateManager directly.
11799  * @param {String/HTMLElement/Roo.Element} el The element to update
11800  * @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).
11801  */
11802 Roo.UpdateManager = function(el, forceNew){
11803     el = Roo.get(el);
11804     if(!forceNew && el.updateManager){
11805         return el.updateManager;
11806     }
11807     /**
11808      * The Element object
11809      * @type Roo.Element
11810      */
11811     this.el = el;
11812     /**
11813      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11814      * @type String
11815      */
11816     this.defaultUrl = null;
11817
11818     this.addEvents({
11819         /**
11820          * @event beforeupdate
11821          * Fired before an update is made, return false from your handler and the update is cancelled.
11822          * @param {Roo.Element} el
11823          * @param {String/Object/Function} url
11824          * @param {String/Object} params
11825          */
11826         "beforeupdate": true,
11827         /**
11828          * @event update
11829          * Fired after successful update is made.
11830          * @param {Roo.Element} el
11831          * @param {Object} oResponseObject The response Object
11832          */
11833         "update": true,
11834         /**
11835          * @event failure
11836          * Fired on update failure.
11837          * @param {Roo.Element} el
11838          * @param {Object} oResponseObject The response Object
11839          */
11840         "failure": true
11841     });
11842     var d = Roo.UpdateManager.defaults;
11843     /**
11844      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11845      * @type String
11846      */
11847     this.sslBlankUrl = d.sslBlankUrl;
11848     /**
11849      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11850      * @type Boolean
11851      */
11852     this.disableCaching = d.disableCaching;
11853     /**
11854      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11855      * @type String
11856      */
11857     this.indicatorText = d.indicatorText;
11858     /**
11859      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11860      * @type String
11861      */
11862     this.showLoadIndicator = d.showLoadIndicator;
11863     /**
11864      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11865      * @type Number
11866      */
11867     this.timeout = d.timeout;
11868
11869     /**
11870      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11871      * @type Boolean
11872      */
11873     this.loadScripts = d.loadScripts;
11874
11875     /**
11876      * Transaction object of current executing transaction
11877      */
11878     this.transaction = null;
11879
11880     /**
11881      * @private
11882      */
11883     this.autoRefreshProcId = null;
11884     /**
11885      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11886      * @type Function
11887      */
11888     this.refreshDelegate = this.refresh.createDelegate(this);
11889     /**
11890      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11891      * @type Function
11892      */
11893     this.updateDelegate = this.update.createDelegate(this);
11894     /**
11895      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11896      * @type Function
11897      */
11898     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11899     /**
11900      * @private
11901      */
11902     this.successDelegate = this.processSuccess.createDelegate(this);
11903     /**
11904      * @private
11905      */
11906     this.failureDelegate = this.processFailure.createDelegate(this);
11907
11908     if(!this.renderer){
11909      /**
11910       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11911       */
11912     this.renderer = new Roo.UpdateManager.BasicRenderer();
11913     }
11914     
11915     Roo.UpdateManager.superclass.constructor.call(this);
11916 };
11917
11918 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11919     /**
11920      * Get the Element this UpdateManager is bound to
11921      * @return {Roo.Element} The element
11922      */
11923     getEl : function(){
11924         return this.el;
11925     },
11926     /**
11927      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11928      * @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:
11929 <pre><code>
11930 um.update({<br/>
11931     url: "your-url.php",<br/>
11932     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11933     callback: yourFunction,<br/>
11934     scope: yourObject, //(optional scope)  <br/>
11935     discardUrl: false, <br/>
11936     nocache: false,<br/>
11937     text: "Loading...",<br/>
11938     timeout: 30,<br/>
11939     scripts: false<br/>
11940 });
11941 </code></pre>
11942      * The only required property is url. The optional properties nocache, text and scripts
11943      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11944      * @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}
11945      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11946      * @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.
11947      */
11948     update : function(url, params, callback, discardUrl){
11949         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11950             var method = this.method,
11951                 cfg;
11952             if(typeof url == "object"){ // must be config object
11953                 cfg = url;
11954                 url = cfg.url;
11955                 params = params || cfg.params;
11956                 callback = callback || cfg.callback;
11957                 discardUrl = discardUrl || cfg.discardUrl;
11958                 if(callback && cfg.scope){
11959                     callback = callback.createDelegate(cfg.scope);
11960                 }
11961                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11962                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11963                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11964                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11965                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11966             }
11967             this.showLoading();
11968             if(!discardUrl){
11969                 this.defaultUrl = url;
11970             }
11971             if(typeof url == "function"){
11972                 url = url.call(this);
11973             }
11974
11975             method = method || (params ? "POST" : "GET");
11976             if(method == "GET"){
11977                 url = this.prepareUrl(url);
11978             }
11979
11980             var o = Roo.apply(cfg ||{}, {
11981                 url : url,
11982                 params: params,
11983                 success: this.successDelegate,
11984                 failure: this.failureDelegate,
11985                 callback: undefined,
11986                 timeout: (this.timeout*1000),
11987                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11988             });
11989             Roo.log("updated manager called with timeout of " + o.timeout);
11990             this.transaction = Roo.Ajax.request(o);
11991         }
11992     },
11993
11994     /**
11995      * 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.
11996      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11997      * @param {String/HTMLElement} form The form Id or form element
11998      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11999      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12000      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12001      */
12002     formUpdate : function(form, url, reset, callback){
12003         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12004             if(typeof url == "function"){
12005                 url = url.call(this);
12006             }
12007             form = Roo.getDom(form);
12008             this.transaction = Roo.Ajax.request({
12009                 form: form,
12010                 url:url,
12011                 success: this.successDelegate,
12012                 failure: this.failureDelegate,
12013                 timeout: (this.timeout*1000),
12014                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12015             });
12016             this.showLoading.defer(1, this);
12017         }
12018     },
12019
12020     /**
12021      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12022      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12023      */
12024     refresh : function(callback){
12025         if(this.defaultUrl == null){
12026             return;
12027         }
12028         this.update(this.defaultUrl, null, callback, true);
12029     },
12030
12031     /**
12032      * Set this element to auto refresh.
12033      * @param {Number} interval How often to update (in seconds).
12034      * @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)
12035      * @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}
12036      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12037      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12038      */
12039     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12040         if(refreshNow){
12041             this.update(url || this.defaultUrl, params, callback, true);
12042         }
12043         if(this.autoRefreshProcId){
12044             clearInterval(this.autoRefreshProcId);
12045         }
12046         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12047     },
12048
12049     /**
12050      * Stop auto refresh on this element.
12051      */
12052      stopAutoRefresh : function(){
12053         if(this.autoRefreshProcId){
12054             clearInterval(this.autoRefreshProcId);
12055             delete this.autoRefreshProcId;
12056         }
12057     },
12058
12059     isAutoRefreshing : function(){
12060        return this.autoRefreshProcId ? true : false;
12061     },
12062     /**
12063      * Called to update the element to "Loading" state. Override to perform custom action.
12064      */
12065     showLoading : function(){
12066         if(this.showLoadIndicator){
12067             this.el.update(this.indicatorText);
12068         }
12069     },
12070
12071     /**
12072      * Adds unique parameter to query string if disableCaching = true
12073      * @private
12074      */
12075     prepareUrl : function(url){
12076         if(this.disableCaching){
12077             var append = "_dc=" + (new Date().getTime());
12078             if(url.indexOf("?") !== -1){
12079                 url += "&" + append;
12080             }else{
12081                 url += "?" + append;
12082             }
12083         }
12084         return url;
12085     },
12086
12087     /**
12088      * @private
12089      */
12090     processSuccess : function(response){
12091         this.transaction = null;
12092         if(response.argument.form && response.argument.reset){
12093             try{ // put in try/catch since some older FF releases had problems with this
12094                 response.argument.form.reset();
12095             }catch(e){}
12096         }
12097         if(this.loadScripts){
12098             this.renderer.render(this.el, response, this,
12099                 this.updateComplete.createDelegate(this, [response]));
12100         }else{
12101             this.renderer.render(this.el, response, this);
12102             this.updateComplete(response);
12103         }
12104     },
12105
12106     updateComplete : function(response){
12107         this.fireEvent("update", this.el, response);
12108         if(typeof response.argument.callback == "function"){
12109             response.argument.callback(this.el, true, response);
12110         }
12111     },
12112
12113     /**
12114      * @private
12115      */
12116     processFailure : function(response){
12117         this.transaction = null;
12118         this.fireEvent("failure", this.el, response);
12119         if(typeof response.argument.callback == "function"){
12120             response.argument.callback(this.el, false, response);
12121         }
12122     },
12123
12124     /**
12125      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12126      * @param {Object} renderer The object implementing the render() method
12127      */
12128     setRenderer : function(renderer){
12129         this.renderer = renderer;
12130     },
12131
12132     getRenderer : function(){
12133        return this.renderer;
12134     },
12135
12136     /**
12137      * Set the defaultUrl used for updates
12138      * @param {String/Function} defaultUrl The url or a function to call to get the url
12139      */
12140     setDefaultUrl : function(defaultUrl){
12141         this.defaultUrl = defaultUrl;
12142     },
12143
12144     /**
12145      * Aborts the executing transaction
12146      */
12147     abort : function(){
12148         if(this.transaction){
12149             Roo.Ajax.abort(this.transaction);
12150         }
12151     },
12152
12153     /**
12154      * Returns true if an update is in progress
12155      * @return {Boolean}
12156      */
12157     isUpdating : function(){
12158         if(this.transaction){
12159             return Roo.Ajax.isLoading(this.transaction);
12160         }
12161         return false;
12162     }
12163 });
12164
12165 /**
12166  * @class Roo.UpdateManager.defaults
12167  * @static (not really - but it helps the doc tool)
12168  * The defaults collection enables customizing the default properties of UpdateManager
12169  */
12170    Roo.UpdateManager.defaults = {
12171        /**
12172          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12173          * @type Number
12174          */
12175          timeout : 30,
12176
12177          /**
12178          * True to process scripts by default (Defaults to false).
12179          * @type Boolean
12180          */
12181         loadScripts : false,
12182
12183         /**
12184         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12185         * @type String
12186         */
12187         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12188         /**
12189          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12190          * @type Boolean
12191          */
12192         disableCaching : false,
12193         /**
12194          * Whether to show indicatorText when loading (Defaults to true).
12195          * @type Boolean
12196          */
12197         showLoadIndicator : true,
12198         /**
12199          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12200          * @type String
12201          */
12202         indicatorText : '<div class="loading-indicator">Loading...</div>'
12203    };
12204
12205 /**
12206  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12207  *Usage:
12208  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12209  * @param {String/HTMLElement/Roo.Element} el The element to update
12210  * @param {String} url The url
12211  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12212  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12213  * @static
12214  * @deprecated
12215  * @member Roo.UpdateManager
12216  */
12217 Roo.UpdateManager.updateElement = function(el, url, params, options){
12218     var um = Roo.get(el, true).getUpdateManager();
12219     Roo.apply(um, options);
12220     um.update(url, params, options ? options.callback : null);
12221 };
12222 // alias for backwards compat
12223 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12224 /**
12225  * @class Roo.UpdateManager.BasicRenderer
12226  * Default Content renderer. Updates the elements innerHTML with the responseText.
12227  */
12228 Roo.UpdateManager.BasicRenderer = function(){};
12229
12230 Roo.UpdateManager.BasicRenderer.prototype = {
12231     /**
12232      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12233      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12234      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12235      * @param {Roo.Element} el The element being rendered
12236      * @param {Object} response The YUI Connect response object
12237      * @param {UpdateManager} updateManager The calling update manager
12238      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12239      */
12240      render : function(el, response, updateManager, callback){
12241         el.update(response.responseText, updateManager.loadScripts, callback);
12242     }
12243 };
12244 /*
12245  * Based on:
12246  * Roo JS
12247  * (c)) Alan Knowles
12248  * Licence : LGPL
12249  */
12250
12251
12252 /**
12253  * @class Roo.DomTemplate
12254  * @extends Roo.Template
12255  * An effort at a dom based template engine..
12256  *
12257  * Similar to XTemplate, except it uses dom parsing to create the template..
12258  *
12259  * Supported features:
12260  *
12261  *  Tags:
12262
12263 <pre><code>
12264       {a_variable} - output encoded.
12265       {a_variable.format:("Y-m-d")} - call a method on the variable
12266       {a_variable:raw} - unencoded output
12267       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12268       {a_variable:this.method_on_template(...)} - call a method on the template object.
12269  
12270 </code></pre>
12271  *  The tpl tag:
12272 <pre><code>
12273         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12274         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12275         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12276         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12277   
12278 </code></pre>
12279  *      
12280  */
12281 Roo.DomTemplate = function()
12282 {
12283      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12284      if (this.html) {
12285         this.compile();
12286      }
12287 };
12288
12289
12290 Roo.extend(Roo.DomTemplate, Roo.Template, {
12291     /**
12292      * id counter for sub templates.
12293      */
12294     id : 0,
12295     /**
12296      * flag to indicate if dom parser is inside a pre,
12297      * it will strip whitespace if not.
12298      */
12299     inPre : false,
12300     
12301     /**
12302      * The various sub templates
12303      */
12304     tpls : false,
12305     
12306     
12307     
12308     /**
12309      *
12310      * basic tag replacing syntax
12311      * WORD:WORD()
12312      *
12313      * // you can fake an object call by doing this
12314      *  x.t:(test,tesT) 
12315      * 
12316      */
12317     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12318     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12319     
12320     iterChild : function (node, method) {
12321         
12322         var oldPre = this.inPre;
12323         if (node.tagName == 'PRE') {
12324             this.inPre = true;
12325         }
12326         for( var i = 0; i < node.childNodes.length; i++) {
12327             method.call(this, node.childNodes[i]);
12328         }
12329         this.inPre = oldPre;
12330     },
12331     
12332     
12333     
12334     /**
12335      * compile the template
12336      *
12337      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12338      *
12339      */
12340     compile: function()
12341     {
12342         var s = this.html;
12343         
12344         // covert the html into DOM...
12345         var doc = false;
12346         var div =false;
12347         try {
12348             doc = document.implementation.createHTMLDocument("");
12349             doc.documentElement.innerHTML =   this.html  ;
12350             div = doc.documentElement;
12351         } catch (e) {
12352             // old IE... - nasty -- it causes all sorts of issues.. with
12353             // images getting pulled from server..
12354             div = document.createElement('div');
12355             div.innerHTML = this.html;
12356         }
12357         //doc.documentElement.innerHTML = htmlBody
12358          
12359         
12360         
12361         this.tpls = [];
12362         var _t = this;
12363         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12364         
12365         var tpls = this.tpls;
12366         
12367         // create a top level template from the snippet..
12368         
12369         //Roo.log(div.innerHTML);
12370         
12371         var tpl = {
12372             uid : 'master',
12373             id : this.id++,
12374             attr : false,
12375             value : false,
12376             body : div.innerHTML,
12377             
12378             forCall : false,
12379             execCall : false,
12380             dom : div,
12381             isTop : true
12382             
12383         };
12384         tpls.unshift(tpl);
12385         
12386         
12387         // compile them...
12388         this.tpls = [];
12389         Roo.each(tpls, function(tp){
12390             this.compileTpl(tp);
12391             this.tpls[tp.id] = tp;
12392         }, this);
12393         
12394         this.master = tpls[0];
12395         return this;
12396         
12397         
12398     },
12399     
12400     compileNode : function(node, istop) {
12401         // test for
12402         //Roo.log(node);
12403         
12404         
12405         // skip anything not a tag..
12406         if (node.nodeType != 1) {
12407             if (node.nodeType == 3 && !this.inPre) {
12408                 // reduce white space..
12409                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12410                 
12411             }
12412             return;
12413         }
12414         
12415         var tpl = {
12416             uid : false,
12417             id : false,
12418             attr : false,
12419             value : false,
12420             body : '',
12421             
12422             forCall : false,
12423             execCall : false,
12424             dom : false,
12425             isTop : istop
12426             
12427             
12428         };
12429         
12430         
12431         switch(true) {
12432             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12433             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12434             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12435             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12436             // no default..
12437         }
12438         
12439         
12440         if (!tpl.attr) {
12441             // just itterate children..
12442             this.iterChild(node,this.compileNode);
12443             return;
12444         }
12445         tpl.uid = this.id++;
12446         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12447         node.removeAttribute('roo-'+ tpl.attr);
12448         if (tpl.attr != 'name') {
12449             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12450             node.parentNode.replaceChild(placeholder,  node);
12451         } else {
12452             
12453             var placeholder =  document.createElement('span');
12454             placeholder.className = 'roo-tpl-' + tpl.value;
12455             node.parentNode.replaceChild(placeholder,  node);
12456         }
12457         
12458         // parent now sees '{domtplXXXX}
12459         this.iterChild(node,this.compileNode);
12460         
12461         // we should now have node body...
12462         var div = document.createElement('div');
12463         div.appendChild(node);
12464         tpl.dom = node;
12465         // this has the unfortunate side effect of converting tagged attributes
12466         // eg. href="{...}" into %7C...%7D
12467         // this has been fixed by searching for those combo's although it's a bit hacky..
12468         
12469         
12470         tpl.body = div.innerHTML;
12471         
12472         
12473          
12474         tpl.id = tpl.uid;
12475         switch(tpl.attr) {
12476             case 'for' :
12477                 switch (tpl.value) {
12478                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12479                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12480                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12481                 }
12482                 break;
12483             
12484             case 'exec':
12485                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12486                 break;
12487             
12488             case 'if':     
12489                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12490                 break;
12491             
12492             case 'name':
12493                 tpl.id  = tpl.value; // replace non characters???
12494                 break;
12495             
12496         }
12497         
12498         
12499         this.tpls.push(tpl);
12500         
12501         
12502         
12503     },
12504     
12505     
12506     
12507     
12508     /**
12509      * Compile a segment of the template into a 'sub-template'
12510      *
12511      * 
12512      * 
12513      *
12514      */
12515     compileTpl : function(tpl)
12516     {
12517         var fm = Roo.util.Format;
12518         var useF = this.disableFormats !== true;
12519         
12520         var sep = Roo.isGecko ? "+\n" : ",\n";
12521         
12522         var undef = function(str) {
12523             Roo.debug && Roo.log("Property not found :"  + str);
12524             return '';
12525         };
12526           
12527         //Roo.log(tpl.body);
12528         
12529         
12530         
12531         var fn = function(m, lbrace, name, format, args)
12532         {
12533             //Roo.log("ARGS");
12534             //Roo.log(arguments);
12535             args = args ? args.replace(/\\'/g,"'") : args;
12536             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12537             if (typeof(format) == 'undefined') {
12538                 format =  'htmlEncode'; 
12539             }
12540             if (format == 'raw' ) {
12541                 format = false;
12542             }
12543             
12544             if(name.substr(0, 6) == 'domtpl'){
12545                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12546             }
12547             
12548             // build an array of options to determine if value is undefined..
12549             
12550             // basically get 'xxxx.yyyy' then do
12551             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12552             //    (function () { Roo.log("Property not found"); return ''; })() :
12553             //    ......
12554             
12555             var udef_ar = [];
12556             var lookfor = '';
12557             Roo.each(name.split('.'), function(st) {
12558                 lookfor += (lookfor.length ? '.': '') + st;
12559                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12560             });
12561             
12562             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12563             
12564             
12565             if(format && useF){
12566                 
12567                 args = args ? ',' + args : "";
12568                  
12569                 if(format.substr(0, 5) != "this."){
12570                     format = "fm." + format + '(';
12571                 }else{
12572                     format = 'this.call("'+ format.substr(5) + '", ';
12573                     args = ", values";
12574                 }
12575                 
12576                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12577             }
12578              
12579             if (args && args.length) {
12580                 // called with xxyx.yuu:(test,test)
12581                 // change to ()
12582                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12583             }
12584             // raw.. - :raw modifier..
12585             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12586             
12587         };
12588         var body;
12589         // branched to use + in gecko and [].join() in others
12590         if(Roo.isGecko){
12591             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12592                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12593                     "';};};";
12594         }else{
12595             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12596             body.push(tpl.body.replace(/(\r\n|\n)/g,
12597                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12598             body.push("'].join('');};};");
12599             body = body.join('');
12600         }
12601         
12602         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12603        
12604         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12605         eval(body);
12606         
12607         return this;
12608     },
12609      
12610     /**
12611      * same as applyTemplate, except it's done to one of the subTemplates
12612      * when using named templates, you can do:
12613      *
12614      * var str = pl.applySubTemplate('your-name', values);
12615      *
12616      * 
12617      * @param {Number} id of the template
12618      * @param {Object} values to apply to template
12619      * @param {Object} parent (normaly the instance of this object)
12620      */
12621     applySubTemplate : function(id, values, parent)
12622     {
12623         
12624         
12625         var t = this.tpls[id];
12626         
12627         
12628         try { 
12629             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12630                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12631                 return '';
12632             }
12633         } catch(e) {
12634             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12635             Roo.log(values);
12636           
12637             return '';
12638         }
12639         try { 
12640             
12641             if(t.execCall && t.execCall.call(this, values, parent)){
12642                 return '';
12643             }
12644         } catch(e) {
12645             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12646             Roo.log(values);
12647             return '';
12648         }
12649         
12650         try {
12651             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12652             parent = t.target ? values : parent;
12653             if(t.forCall && vs instanceof Array){
12654                 var buf = [];
12655                 for(var i = 0, len = vs.length; i < len; i++){
12656                     try {
12657                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12658                     } catch (e) {
12659                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12660                         Roo.log(e.body);
12661                         //Roo.log(t.compiled);
12662                         Roo.log(vs[i]);
12663                     }   
12664                 }
12665                 return buf.join('');
12666             }
12667         } catch (e) {
12668             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12669             Roo.log(values);
12670             return '';
12671         }
12672         try {
12673             return t.compiled.call(this, vs, parent);
12674         } catch (e) {
12675             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12676             Roo.log(e.body);
12677             //Roo.log(t.compiled);
12678             Roo.log(values);
12679             return '';
12680         }
12681     },
12682
12683    
12684
12685     applyTemplate : function(values){
12686         return this.master.compiled.call(this, values, {});
12687         //var s = this.subs;
12688     },
12689
12690     apply : function(){
12691         return this.applyTemplate.apply(this, arguments);
12692     }
12693
12694  });
12695
12696 Roo.DomTemplate.from = function(el){
12697     el = Roo.getDom(el);
12698     return new Roo.Domtemplate(el.value || el.innerHTML);
12699 };/*
12700  * Based on:
12701  * Ext JS Library 1.1.1
12702  * Copyright(c) 2006-2007, Ext JS, LLC.
12703  *
12704  * Originally Released Under LGPL - original licence link has changed is not relivant.
12705  *
12706  * Fork - LGPL
12707  * <script type="text/javascript">
12708  */
12709
12710 /**
12711  * @class Roo.util.DelayedTask
12712  * Provides a convenient method of performing setTimeout where a new
12713  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12714  * You can use this class to buffer
12715  * the keypress events for a certain number of milliseconds, and perform only if they stop
12716  * for that amount of time.
12717  * @constructor The parameters to this constructor serve as defaults and are not required.
12718  * @param {Function} fn (optional) The default function to timeout
12719  * @param {Object} scope (optional) The default scope of that timeout
12720  * @param {Array} args (optional) The default Array of arguments
12721  */
12722 Roo.util.DelayedTask = function(fn, scope, args){
12723     var id = null, d, t;
12724
12725     var call = function(){
12726         var now = new Date().getTime();
12727         if(now - t >= d){
12728             clearInterval(id);
12729             id = null;
12730             fn.apply(scope, args || []);
12731         }
12732     };
12733     /**
12734      * Cancels any pending timeout and queues a new one
12735      * @param {Number} delay The milliseconds to delay
12736      * @param {Function} newFn (optional) Overrides function passed to constructor
12737      * @param {Object} newScope (optional) Overrides scope passed to constructor
12738      * @param {Array} newArgs (optional) Overrides args passed to constructor
12739      */
12740     this.delay = function(delay, newFn, newScope, newArgs){
12741         if(id && delay != d){
12742             this.cancel();
12743         }
12744         d = delay;
12745         t = new Date().getTime();
12746         fn = newFn || fn;
12747         scope = newScope || scope;
12748         args = newArgs || args;
12749         if(!id){
12750             id = setInterval(call, d);
12751         }
12752     };
12753
12754     /**
12755      * Cancel the last queued timeout
12756      */
12757     this.cancel = function(){
12758         if(id){
12759             clearInterval(id);
12760             id = null;
12761         }
12762     };
12763 };/*
12764  * Based on:
12765  * Ext JS Library 1.1.1
12766  * Copyright(c) 2006-2007, Ext JS, LLC.
12767  *
12768  * Originally Released Under LGPL - original licence link has changed is not relivant.
12769  *
12770  * Fork - LGPL
12771  * <script type="text/javascript">
12772  */
12773  
12774  
12775 Roo.util.TaskRunner = function(interval){
12776     interval = interval || 10;
12777     var tasks = [], removeQueue = [];
12778     var id = 0;
12779     var running = false;
12780
12781     var stopThread = function(){
12782         running = false;
12783         clearInterval(id);
12784         id = 0;
12785     };
12786
12787     var startThread = function(){
12788         if(!running){
12789             running = true;
12790             id = setInterval(runTasks, interval);
12791         }
12792     };
12793
12794     var removeTask = function(task){
12795         removeQueue.push(task);
12796         if(task.onStop){
12797             task.onStop();
12798         }
12799     };
12800
12801     var runTasks = function(){
12802         if(removeQueue.length > 0){
12803             for(var i = 0, len = removeQueue.length; i < len; i++){
12804                 tasks.remove(removeQueue[i]);
12805             }
12806             removeQueue = [];
12807             if(tasks.length < 1){
12808                 stopThread();
12809                 return;
12810             }
12811         }
12812         var now = new Date().getTime();
12813         for(var i = 0, len = tasks.length; i < len; ++i){
12814             var t = tasks[i];
12815             var itime = now - t.taskRunTime;
12816             if(t.interval <= itime){
12817                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12818                 t.taskRunTime = now;
12819                 if(rt === false || t.taskRunCount === t.repeat){
12820                     removeTask(t);
12821                     return;
12822                 }
12823             }
12824             if(t.duration && t.duration <= (now - t.taskStartTime)){
12825                 removeTask(t);
12826             }
12827         }
12828     };
12829
12830     /**
12831      * Queues a new task.
12832      * @param {Object} task
12833      */
12834     this.start = function(task){
12835         tasks.push(task);
12836         task.taskStartTime = new Date().getTime();
12837         task.taskRunTime = 0;
12838         task.taskRunCount = 0;
12839         startThread();
12840         return task;
12841     };
12842
12843     this.stop = function(task){
12844         removeTask(task);
12845         return task;
12846     };
12847
12848     this.stopAll = function(){
12849         stopThread();
12850         for(var i = 0, len = tasks.length; i < len; i++){
12851             if(tasks[i].onStop){
12852                 tasks[i].onStop();
12853             }
12854         }
12855         tasks = [];
12856         removeQueue = [];
12857     };
12858 };
12859
12860 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12861  * Based on:
12862  * Ext JS Library 1.1.1
12863  * Copyright(c) 2006-2007, Ext JS, LLC.
12864  *
12865  * Originally Released Under LGPL - original licence link has changed is not relivant.
12866  *
12867  * Fork - LGPL
12868  * <script type="text/javascript">
12869  */
12870
12871  
12872 /**
12873  * @class Roo.util.MixedCollection
12874  * @extends Roo.util.Observable
12875  * A Collection class that maintains both numeric indexes and keys and exposes events.
12876  * @constructor
12877  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12878  * collection (defaults to false)
12879  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12880  * and return the key value for that item.  This is used when available to look up the key on items that
12881  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12882  * equivalent to providing an implementation for the {@link #getKey} method.
12883  */
12884 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12885     this.items = [];
12886     this.map = {};
12887     this.keys = [];
12888     this.length = 0;
12889     this.addEvents({
12890         /**
12891          * @event clear
12892          * Fires when the collection is cleared.
12893          */
12894         "clear" : true,
12895         /**
12896          * @event add
12897          * Fires when an item is added to the collection.
12898          * @param {Number} index The index at which the item was added.
12899          * @param {Object} o The item added.
12900          * @param {String} key The key associated with the added item.
12901          */
12902         "add" : true,
12903         /**
12904          * @event replace
12905          * Fires when an item is replaced in the collection.
12906          * @param {String} key he key associated with the new added.
12907          * @param {Object} old The item being replaced.
12908          * @param {Object} new The new item.
12909          */
12910         "replace" : true,
12911         /**
12912          * @event remove
12913          * Fires when an item is removed from the collection.
12914          * @param {Object} o The item being removed.
12915          * @param {String} key (optional) The key associated with the removed item.
12916          */
12917         "remove" : true,
12918         "sort" : true
12919     });
12920     this.allowFunctions = allowFunctions === true;
12921     if(keyFn){
12922         this.getKey = keyFn;
12923     }
12924     Roo.util.MixedCollection.superclass.constructor.call(this);
12925 };
12926
12927 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12928     allowFunctions : false,
12929     
12930 /**
12931  * Adds an item to the collection.
12932  * @param {String} key The key to associate with the item
12933  * @param {Object} o The item to add.
12934  * @return {Object} The item added.
12935  */
12936     add : function(key, o){
12937         if(arguments.length == 1){
12938             o = arguments[0];
12939             key = this.getKey(o);
12940         }
12941         if(typeof key == "undefined" || key === null){
12942             this.length++;
12943             this.items.push(o);
12944             this.keys.push(null);
12945         }else{
12946             var old = this.map[key];
12947             if(old){
12948                 return this.replace(key, o);
12949             }
12950             this.length++;
12951             this.items.push(o);
12952             this.map[key] = o;
12953             this.keys.push(key);
12954         }
12955         this.fireEvent("add", this.length-1, o, key);
12956         return o;
12957     },
12958        
12959 /**
12960   * MixedCollection has a generic way to fetch keys if you implement getKey.
12961 <pre><code>
12962 // normal way
12963 var mc = new Roo.util.MixedCollection();
12964 mc.add(someEl.dom.id, someEl);
12965 mc.add(otherEl.dom.id, otherEl);
12966 //and so on
12967
12968 // using getKey
12969 var mc = new Roo.util.MixedCollection();
12970 mc.getKey = function(el){
12971    return el.dom.id;
12972 };
12973 mc.add(someEl);
12974 mc.add(otherEl);
12975
12976 // or via the constructor
12977 var mc = new Roo.util.MixedCollection(false, function(el){
12978    return el.dom.id;
12979 });
12980 mc.add(someEl);
12981 mc.add(otherEl);
12982 </code></pre>
12983  * @param o {Object} The item for which to find the key.
12984  * @return {Object} The key for the passed item.
12985  */
12986     getKey : function(o){
12987          return o.id; 
12988     },
12989    
12990 /**
12991  * Replaces an item in the collection.
12992  * @param {String} key The key associated with the item to replace, or the item to replace.
12993  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12994  * @return {Object}  The new item.
12995  */
12996     replace : function(key, o){
12997         if(arguments.length == 1){
12998             o = arguments[0];
12999             key = this.getKey(o);
13000         }
13001         var old = this.item(key);
13002         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13003              return this.add(key, o);
13004         }
13005         var index = this.indexOfKey(key);
13006         this.items[index] = o;
13007         this.map[key] = o;
13008         this.fireEvent("replace", key, old, o);
13009         return o;
13010     },
13011    
13012 /**
13013  * Adds all elements of an Array or an Object to the collection.
13014  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13015  * an Array of values, each of which are added to the collection.
13016  */
13017     addAll : function(objs){
13018         if(arguments.length > 1 || objs instanceof Array){
13019             var args = arguments.length > 1 ? arguments : objs;
13020             for(var i = 0, len = args.length; i < len; i++){
13021                 this.add(args[i]);
13022             }
13023         }else{
13024             for(var key in objs){
13025                 if(this.allowFunctions || typeof objs[key] != "function"){
13026                     this.add(key, objs[key]);
13027                 }
13028             }
13029         }
13030     },
13031    
13032 /**
13033  * Executes the specified function once for every item in the collection, passing each
13034  * item as the first and only parameter. returning false from the function will stop the iteration.
13035  * @param {Function} fn The function to execute for each item.
13036  * @param {Object} scope (optional) The scope in which to execute the function.
13037  */
13038     each : function(fn, scope){
13039         var items = [].concat(this.items); // each safe for removal
13040         for(var i = 0, len = items.length; i < len; i++){
13041             if(fn.call(scope || items[i], items[i], i, len) === false){
13042                 break;
13043             }
13044         }
13045     },
13046    
13047 /**
13048  * Executes the specified function once for every key in the collection, passing each
13049  * key, and its associated item as the first two parameters.
13050  * @param {Function} fn The function to execute for each item.
13051  * @param {Object} scope (optional) The scope in which to execute the function.
13052  */
13053     eachKey : function(fn, scope){
13054         for(var i = 0, len = this.keys.length; i < len; i++){
13055             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13056         }
13057     },
13058    
13059 /**
13060  * Returns the first item in the collection which elicits a true return value from the
13061  * passed selection function.
13062  * @param {Function} fn The selection function to execute for each item.
13063  * @param {Object} scope (optional) The scope in which to execute the function.
13064  * @return {Object} The first item in the collection which returned true from the selection function.
13065  */
13066     find : function(fn, scope){
13067         for(var i = 0, len = this.items.length; i < len; i++){
13068             if(fn.call(scope || window, this.items[i], this.keys[i])){
13069                 return this.items[i];
13070             }
13071         }
13072         return null;
13073     },
13074    
13075 /**
13076  * Inserts an item at the specified index in the collection.
13077  * @param {Number} index The index to insert the item at.
13078  * @param {String} key The key to associate with the new item, or the item itself.
13079  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13080  * @return {Object} The item inserted.
13081  */
13082     insert : function(index, key, o){
13083         if(arguments.length == 2){
13084             o = arguments[1];
13085             key = this.getKey(o);
13086         }
13087         if(index >= this.length){
13088             return this.add(key, o);
13089         }
13090         this.length++;
13091         this.items.splice(index, 0, o);
13092         if(typeof key != "undefined" && key != null){
13093             this.map[key] = o;
13094         }
13095         this.keys.splice(index, 0, key);
13096         this.fireEvent("add", index, o, key);
13097         return o;
13098     },
13099    
13100 /**
13101  * Removed an item from the collection.
13102  * @param {Object} o The item to remove.
13103  * @return {Object} The item removed.
13104  */
13105     remove : function(o){
13106         return this.removeAt(this.indexOf(o));
13107     },
13108    
13109 /**
13110  * Remove an item from a specified index in the collection.
13111  * @param {Number} index The index within the collection of the item to remove.
13112  */
13113     removeAt : function(index){
13114         if(index < this.length && index >= 0){
13115             this.length--;
13116             var o = this.items[index];
13117             this.items.splice(index, 1);
13118             var key = this.keys[index];
13119             if(typeof key != "undefined"){
13120                 delete this.map[key];
13121             }
13122             this.keys.splice(index, 1);
13123             this.fireEvent("remove", o, key);
13124         }
13125     },
13126    
13127 /**
13128  * Removed an item associated with the passed key fom the collection.
13129  * @param {String} key The key of the item to remove.
13130  */
13131     removeKey : function(key){
13132         return this.removeAt(this.indexOfKey(key));
13133     },
13134    
13135 /**
13136  * Returns the number of items in the collection.
13137  * @return {Number} the number of items in the collection.
13138  */
13139     getCount : function(){
13140         return this.length; 
13141     },
13142    
13143 /**
13144  * Returns index within the collection of the passed Object.
13145  * @param {Object} o The item to find the index of.
13146  * @return {Number} index of the item.
13147  */
13148     indexOf : function(o){
13149         if(!this.items.indexOf){
13150             for(var i = 0, len = this.items.length; i < len; i++){
13151                 if(this.items[i] == o) {
13152                     return i;
13153                 }
13154             }
13155             return -1;
13156         }else{
13157             return this.items.indexOf(o);
13158         }
13159     },
13160    
13161 /**
13162  * Returns index within the collection of the passed key.
13163  * @param {String} key The key to find the index of.
13164  * @return {Number} index of the key.
13165  */
13166     indexOfKey : function(key){
13167         if(!this.keys.indexOf){
13168             for(var i = 0, len = this.keys.length; i < len; i++){
13169                 if(this.keys[i] == key) {
13170                     return i;
13171                 }
13172             }
13173             return -1;
13174         }else{
13175             return this.keys.indexOf(key);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the item associated with the passed key OR index. Key has priority over index.
13181  * @param {String/Number} key The key or index of the item.
13182  * @return {Object} The item associated with the passed key.
13183  */
13184     item : function(key){
13185         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13186         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13187     },
13188     
13189 /**
13190  * Returns the item at the specified index.
13191  * @param {Number} index The index of the item.
13192  * @return {Object}
13193  */
13194     itemAt : function(index){
13195         return this.items[index];
13196     },
13197     
13198 /**
13199  * Returns the item associated with the passed key.
13200  * @param {String/Number} key The key of the item.
13201  * @return {Object} The item associated with the passed key.
13202  */
13203     key : function(key){
13204         return this.map[key];
13205     },
13206    
13207 /**
13208  * Returns true if the collection contains the passed Object as an item.
13209  * @param {Object} o  The Object to look for in the collection.
13210  * @return {Boolean} True if the collection contains the Object as an item.
13211  */
13212     contains : function(o){
13213         return this.indexOf(o) != -1;
13214     },
13215    
13216 /**
13217  * Returns true if the collection contains the passed Object as a key.
13218  * @param {String} key The key to look for in the collection.
13219  * @return {Boolean} True if the collection contains the Object as a key.
13220  */
13221     containsKey : function(key){
13222         return typeof this.map[key] != "undefined";
13223     },
13224    
13225 /**
13226  * Removes all items from the collection.
13227  */
13228     clear : function(){
13229         this.length = 0;
13230         this.items = [];
13231         this.keys = [];
13232         this.map = {};
13233         this.fireEvent("clear");
13234     },
13235    
13236 /**
13237  * Returns the first item in the collection.
13238  * @return {Object} the first item in the collection..
13239  */
13240     first : function(){
13241         return this.items[0]; 
13242     },
13243    
13244 /**
13245  * Returns the last item in the collection.
13246  * @return {Object} the last item in the collection..
13247  */
13248     last : function(){
13249         return this.items[this.length-1];   
13250     },
13251     
13252     _sort : function(property, dir, fn){
13253         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13254         fn = fn || function(a, b){
13255             return a-b;
13256         };
13257         var c = [], k = this.keys, items = this.items;
13258         for(var i = 0, len = items.length; i < len; i++){
13259             c[c.length] = {key: k[i], value: items[i], index: i};
13260         }
13261         c.sort(function(a, b){
13262             var v = fn(a[property], b[property]) * dsc;
13263             if(v == 0){
13264                 v = (a.index < b.index ? -1 : 1);
13265             }
13266             return v;
13267         });
13268         for(var i = 0, len = c.length; i < len; i++){
13269             items[i] = c[i].value;
13270             k[i] = c[i].key;
13271         }
13272         this.fireEvent("sort", this);
13273     },
13274     
13275     /**
13276      * Sorts this collection with the passed comparison function
13277      * @param {String} direction (optional) "ASC" or "DESC"
13278      * @param {Function} fn (optional) comparison function
13279      */
13280     sort : function(dir, fn){
13281         this._sort("value", dir, fn);
13282     },
13283     
13284     /**
13285      * Sorts this collection by keys
13286      * @param {String} direction (optional) "ASC" or "DESC"
13287      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13288      */
13289     keySort : function(dir, fn){
13290         this._sort("key", dir, fn || function(a, b){
13291             return String(a).toUpperCase()-String(b).toUpperCase();
13292         });
13293     },
13294     
13295     /**
13296      * Returns a range of items in this collection
13297      * @param {Number} startIndex (optional) defaults to 0
13298      * @param {Number} endIndex (optional) default to the last item
13299      * @return {Array} An array of items
13300      */
13301     getRange : function(start, end){
13302         var items = this.items;
13303         if(items.length < 1){
13304             return [];
13305         }
13306         start = start || 0;
13307         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13308         var r = [];
13309         if(start <= end){
13310             for(var i = start; i <= end; i++) {
13311                     r[r.length] = items[i];
13312             }
13313         }else{
13314             for(var i = start; i >= end; i--) {
13315                     r[r.length] = items[i];
13316             }
13317         }
13318         return r;
13319     },
13320         
13321     /**
13322      * Filter the <i>objects</i> in this collection by a specific property. 
13323      * Returns a new collection that has been filtered.
13324      * @param {String} property A property on your objects
13325      * @param {String/RegExp} value Either string that the property values 
13326      * should start with or a RegExp to test against the property
13327      * @return {MixedCollection} The new filtered collection
13328      */
13329     filter : function(property, value){
13330         if(!value.exec){ // not a regex
13331             value = String(value);
13332             if(value.length == 0){
13333                 return this.clone();
13334             }
13335             value = new RegExp("^" + Roo.escapeRe(value), "i");
13336         }
13337         return this.filterBy(function(o){
13338             return o && value.test(o[property]);
13339         });
13340         },
13341     
13342     /**
13343      * Filter by a function. * Returns a new collection that has been filtered.
13344      * The passed function will be called with each 
13345      * object in the collection. If the function returns true, the value is included 
13346      * otherwise it is filtered.
13347      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13348      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13349      * @return {MixedCollection} The new filtered collection
13350      */
13351     filterBy : function(fn, scope){
13352         var r = new Roo.util.MixedCollection();
13353         r.getKey = this.getKey;
13354         var k = this.keys, it = this.items;
13355         for(var i = 0, len = it.length; i < len; i++){
13356             if(fn.call(scope||this, it[i], k[i])){
13357                                 r.add(k[i], it[i]);
13358                         }
13359         }
13360         return r;
13361     },
13362     
13363     /**
13364      * Creates a duplicate of this collection
13365      * @return {MixedCollection}
13366      */
13367     clone : function(){
13368         var r = new Roo.util.MixedCollection();
13369         var k = this.keys, it = this.items;
13370         for(var i = 0, len = it.length; i < len; i++){
13371             r.add(k[i], it[i]);
13372         }
13373         r.getKey = this.getKey;
13374         return r;
13375     }
13376 });
13377 /**
13378  * Returns the item associated with the passed key or index.
13379  * @method
13380  * @param {String/Number} key The key or index of the item.
13381  * @return {Object} The item associated with the passed key.
13382  */
13383 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13384  * Based on:
13385  * Ext JS Library 1.1.1
13386  * Copyright(c) 2006-2007, Ext JS, LLC.
13387  *
13388  * Originally Released Under LGPL - original licence link has changed is not relivant.
13389  *
13390  * Fork - LGPL
13391  * <script type="text/javascript">
13392  */
13393 /**
13394  * @class Roo.util.JSON
13395  * Modified version of Douglas Crockford"s json.js that doesn"t
13396  * mess with the Object prototype 
13397  * http://www.json.org/js.html
13398  * @singleton
13399  */
13400 Roo.util.JSON = new (function(){
13401     var useHasOwn = {}.hasOwnProperty ? true : false;
13402     
13403     // crashes Safari in some instances
13404     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13405     
13406     var pad = function(n) {
13407         return n < 10 ? "0" + n : n;
13408     };
13409     
13410     var m = {
13411         "\b": '\\b',
13412         "\t": '\\t',
13413         "\n": '\\n',
13414         "\f": '\\f',
13415         "\r": '\\r',
13416         '"' : '\\"',
13417         "\\": '\\\\'
13418     };
13419
13420     var encodeString = function(s){
13421         if (/["\\\x00-\x1f]/.test(s)) {
13422             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13423                 var c = m[b];
13424                 if(c){
13425                     return c;
13426                 }
13427                 c = b.charCodeAt();
13428                 return "\\u00" +
13429                     Math.floor(c / 16).toString(16) +
13430                     (c % 16).toString(16);
13431             }) + '"';
13432         }
13433         return '"' + s + '"';
13434     };
13435     
13436     var encodeArray = function(o){
13437         var a = ["["], b, i, l = o.length, v;
13438             for (i = 0; i < l; i += 1) {
13439                 v = o[i];
13440                 switch (typeof v) {
13441                     case "undefined":
13442                     case "function":
13443                     case "unknown":
13444                         break;
13445                     default:
13446                         if (b) {
13447                             a.push(',');
13448                         }
13449                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13450                         b = true;
13451                 }
13452             }
13453             a.push("]");
13454             return a.join("");
13455     };
13456     
13457     var encodeDate = function(o){
13458         return '"' + o.getFullYear() + "-" +
13459                 pad(o.getMonth() + 1) + "-" +
13460                 pad(o.getDate()) + "T" +
13461                 pad(o.getHours()) + ":" +
13462                 pad(o.getMinutes()) + ":" +
13463                 pad(o.getSeconds()) + '"';
13464     };
13465     
13466     /**
13467      * Encodes an Object, Array or other value
13468      * @param {Mixed} o The variable to encode
13469      * @return {String} The JSON string
13470      */
13471     this.encode = function(o)
13472     {
13473         // should this be extended to fully wrap stringify..
13474         
13475         if(typeof o == "undefined" || o === null){
13476             return "null";
13477         }else if(o instanceof Array){
13478             return encodeArray(o);
13479         }else if(o instanceof Date){
13480             return encodeDate(o);
13481         }else if(typeof o == "string"){
13482             return encodeString(o);
13483         }else if(typeof o == "number"){
13484             return isFinite(o) ? String(o) : "null";
13485         }else if(typeof o == "boolean"){
13486             return String(o);
13487         }else {
13488             var a = ["{"], b, i, v;
13489             for (i in o) {
13490                 if(!useHasOwn || o.hasOwnProperty(i)) {
13491                     v = o[i];
13492                     switch (typeof v) {
13493                     case "undefined":
13494                     case "function":
13495                     case "unknown":
13496                         break;
13497                     default:
13498                         if(b){
13499                             a.push(',');
13500                         }
13501                         a.push(this.encode(i), ":",
13502                                 v === null ? "null" : this.encode(v));
13503                         b = true;
13504                     }
13505                 }
13506             }
13507             a.push("}");
13508             return a.join("");
13509         }
13510     };
13511     
13512     /**
13513      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13514      * @param {String} json The JSON string
13515      * @return {Object} The resulting object
13516      */
13517     this.decode = function(json){
13518         
13519         return  /** eval:var:json */ eval("(" + json + ')');
13520     };
13521 })();
13522 /** 
13523  * Shorthand for {@link Roo.util.JSON#encode}
13524  * @member Roo encode 
13525  * @method */
13526 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13527 /** 
13528  * Shorthand for {@link Roo.util.JSON#decode}
13529  * @member Roo decode 
13530  * @method */
13531 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13532 /*
13533  * Based on:
13534  * Ext JS Library 1.1.1
13535  * Copyright(c) 2006-2007, Ext JS, LLC.
13536  *
13537  * Originally Released Under LGPL - original licence link has changed is not relivant.
13538  *
13539  * Fork - LGPL
13540  * <script type="text/javascript">
13541  */
13542  
13543 /**
13544  * @class Roo.util.Format
13545  * Reusable data formatting functions
13546  * @singleton
13547  */
13548 Roo.util.Format = function(){
13549     var trimRe = /^\s+|\s+$/g;
13550     return {
13551         /**
13552          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13553          * @param {String} value The string to truncate
13554          * @param {Number} length The maximum length to allow before truncating
13555          * @return {String} The converted text
13556          */
13557         ellipsis : function(value, len){
13558             if(value && value.length > len){
13559                 return value.substr(0, len-3)+"...";
13560             }
13561             return value;
13562         },
13563
13564         /**
13565          * Checks a reference and converts it to empty string if it is undefined
13566          * @param {Mixed} value Reference to check
13567          * @return {Mixed} Empty string if converted, otherwise the original value
13568          */
13569         undef : function(value){
13570             return typeof value != "undefined" ? value : "";
13571         },
13572
13573         /**
13574          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13575          * @param {String} value The string to encode
13576          * @return {String} The encoded text
13577          */
13578         htmlEncode : function(value){
13579             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13580         },
13581
13582         /**
13583          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13584          * @param {String} value The string to decode
13585          * @return {String} The decoded text
13586          */
13587         htmlDecode : function(value){
13588             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13589         },
13590
13591         /**
13592          * Trims any whitespace from either side of a string
13593          * @param {String} value The text to trim
13594          * @return {String} The trimmed text
13595          */
13596         trim : function(value){
13597             return String(value).replace(trimRe, "");
13598         },
13599
13600         /**
13601          * Returns a substring from within an original string
13602          * @param {String} value The original text
13603          * @param {Number} start The start index of the substring
13604          * @param {Number} length The length of the substring
13605          * @return {String} The substring
13606          */
13607         substr : function(value, start, length){
13608             return String(value).substr(start, length);
13609         },
13610
13611         /**
13612          * Converts a string to all lower case letters
13613          * @param {String} value The text to convert
13614          * @return {String} The converted text
13615          */
13616         lowercase : function(value){
13617             return String(value).toLowerCase();
13618         },
13619
13620         /**
13621          * Converts a string to all upper case letters
13622          * @param {String} value The text to convert
13623          * @return {String} The converted text
13624          */
13625         uppercase : function(value){
13626             return String(value).toUpperCase();
13627         },
13628
13629         /**
13630          * Converts the first character only of a string to upper case
13631          * @param {String} value The text to convert
13632          * @return {String} The converted text
13633          */
13634         capitalize : function(value){
13635             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13636         },
13637
13638         // private
13639         call : function(value, fn){
13640             if(arguments.length > 2){
13641                 var args = Array.prototype.slice.call(arguments, 2);
13642                 args.unshift(value);
13643                  
13644                 return /** eval:var:value */  eval(fn).apply(window, args);
13645             }else{
13646                 /** eval:var:value */
13647                 return /** eval:var:value */ eval(fn).call(window, value);
13648             }
13649         },
13650
13651        
13652         /**
13653          * safer version of Math.toFixed..??/
13654          * @param {Number/String} value The numeric value to format
13655          * @param {Number/String} value Decimal places 
13656          * @return {String} The formatted currency string
13657          */
13658         toFixed : function(v, n)
13659         {
13660             // why not use to fixed - precision is buggered???
13661             if (!n) {
13662                 return Math.round(v-0);
13663             }
13664             var fact = Math.pow(10,n+1);
13665             v = (Math.round((v-0)*fact))/fact;
13666             var z = (''+fact).substring(2);
13667             if (v == Math.floor(v)) {
13668                 return Math.floor(v) + '.' + z;
13669             }
13670             
13671             // now just padd decimals..
13672             var ps = String(v).split('.');
13673             var fd = (ps[1] + z);
13674             var r = fd.substring(0,n); 
13675             var rm = fd.substring(n); 
13676             if (rm < 5) {
13677                 return ps[0] + '.' + r;
13678             }
13679             r*=1; // turn it into a number;
13680             r++;
13681             if (String(r).length != n) {
13682                 ps[0]*=1;
13683                 ps[0]++;
13684                 r = String(r).substring(1); // chop the end off.
13685             }
13686             
13687             return ps[0] + '.' + r;
13688              
13689         },
13690         
13691         /**
13692          * Format a number as US currency
13693          * @param {Number/String} value The numeric value to format
13694          * @return {String} The formatted currency string
13695          */
13696         usMoney : function(v){
13697             return '$' + Roo.util.Format.number(v);
13698         },
13699         
13700         /**
13701          * Format a number
13702          * eventually this should probably emulate php's number_format
13703          * @param {Number/String} value The numeric value to format
13704          * @param {Number} decimals number of decimal places
13705          * @return {String} The formatted currency string
13706          */
13707         number : function(v,decimals)
13708         {
13709             // multiply and round.
13710             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13711             var mul = Math.pow(10, decimals);
13712             var zero = String(mul).substring(1);
13713             v = (Math.round((v-0)*mul))/mul;
13714             
13715             // if it's '0' number.. then
13716             
13717             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13718             v = String(v);
13719             var ps = v.split('.');
13720             var whole = ps[0];
13721             
13722             
13723             var r = /(\d+)(\d{3})/;
13724             // add comma's
13725             while (r.test(whole)) {
13726                 whole = whole.replace(r, '$1' + ',' + '$2');
13727             }
13728             
13729             
13730             var sub = ps[1] ?
13731                     // has decimals..
13732                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13733                     // does not have decimals
13734                     (decimals ? ('.' + zero) : '');
13735             
13736             
13737             return whole + sub ;
13738         },
13739         
13740         /**
13741          * Parse a value into a formatted date using the specified format pattern.
13742          * @param {Mixed} value The value to format
13743          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13744          * @return {String} The formatted date string
13745          */
13746         date : function(v, format){
13747             if(!v){
13748                 return "";
13749             }
13750             if(!(v instanceof Date)){
13751                 v = new Date(Date.parse(v));
13752             }
13753             return v.dateFormat(format || Roo.util.Format.defaults.date);
13754         },
13755
13756         /**
13757          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13758          * @param {String} format Any valid date format string
13759          * @return {Function} The date formatting function
13760          */
13761         dateRenderer : function(format){
13762             return function(v){
13763                 return Roo.util.Format.date(v, format);  
13764             };
13765         },
13766
13767         // private
13768         stripTagsRE : /<\/?[^>]+>/gi,
13769         
13770         /**
13771          * Strips all HTML tags
13772          * @param {Mixed} value The text from which to strip tags
13773          * @return {String} The stripped text
13774          */
13775         stripTags : function(v){
13776             return !v ? v : String(v).replace(this.stripTagsRE, "");
13777         }
13778     };
13779 }();
13780 Roo.util.Format.defaults = {
13781     date : 'd/M/Y'
13782 };/*
13783  * Based on:
13784  * Ext JS Library 1.1.1
13785  * Copyright(c) 2006-2007, Ext JS, LLC.
13786  *
13787  * Originally Released Under LGPL - original licence link has changed is not relivant.
13788  *
13789  * Fork - LGPL
13790  * <script type="text/javascript">
13791  */
13792
13793
13794  
13795
13796 /**
13797  * @class Roo.MasterTemplate
13798  * @extends Roo.Template
13799  * Provides a template that can have child templates. The syntax is:
13800 <pre><code>
13801 var t = new Roo.MasterTemplate(
13802         '&lt;select name="{name}"&gt;',
13803                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13804         '&lt;/select&gt;'
13805 );
13806 t.add('options', {value: 'foo', text: 'bar'});
13807 // or you can add multiple child elements in one shot
13808 t.addAll('options', [
13809     {value: 'foo', text: 'bar'},
13810     {value: 'foo2', text: 'bar2'},
13811     {value: 'foo3', text: 'bar3'}
13812 ]);
13813 // then append, applying the master template values
13814 t.append('my-form', {name: 'my-select'});
13815 </code></pre>
13816 * A name attribute for the child template is not required if you have only one child
13817 * template or you want to refer to them by index.
13818  */
13819 Roo.MasterTemplate = function(){
13820     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13821     this.originalHtml = this.html;
13822     var st = {};
13823     var m, re = this.subTemplateRe;
13824     re.lastIndex = 0;
13825     var subIndex = 0;
13826     while(m = re.exec(this.html)){
13827         var name = m[1], content = m[2];
13828         st[subIndex] = {
13829             name: name,
13830             index: subIndex,
13831             buffer: [],
13832             tpl : new Roo.Template(content)
13833         };
13834         if(name){
13835             st[name] = st[subIndex];
13836         }
13837         st[subIndex].tpl.compile();
13838         st[subIndex].tpl.call = this.call.createDelegate(this);
13839         subIndex++;
13840     }
13841     this.subCount = subIndex;
13842     this.subs = st;
13843 };
13844 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13845     /**
13846     * The regular expression used to match sub templates
13847     * @type RegExp
13848     * @property
13849     */
13850     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13851
13852     /**
13853      * Applies the passed values to a child template.
13854      * @param {String/Number} name (optional) The name or index of the child template
13855      * @param {Array/Object} values The values to be applied to the template
13856      * @return {MasterTemplate} this
13857      */
13858      add : function(name, values){
13859         if(arguments.length == 1){
13860             values = arguments[0];
13861             name = 0;
13862         }
13863         var s = this.subs[name];
13864         s.buffer[s.buffer.length] = s.tpl.apply(values);
13865         return this;
13866     },
13867
13868     /**
13869      * Applies all the passed values to a child template.
13870      * @param {String/Number} name (optional) The name or index of the child template
13871      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13872      * @param {Boolean} reset (optional) True to reset the template first
13873      * @return {MasterTemplate} this
13874      */
13875     fill : function(name, values, reset){
13876         var a = arguments;
13877         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13878             values = a[0];
13879             name = 0;
13880             reset = a[1];
13881         }
13882         if(reset){
13883             this.reset();
13884         }
13885         for(var i = 0, len = values.length; i < len; i++){
13886             this.add(name, values[i]);
13887         }
13888         return this;
13889     },
13890
13891     /**
13892      * Resets the template for reuse
13893      * @return {MasterTemplate} this
13894      */
13895      reset : function(){
13896         var s = this.subs;
13897         for(var i = 0; i < this.subCount; i++){
13898             s[i].buffer = [];
13899         }
13900         return this;
13901     },
13902
13903     applyTemplate : function(values){
13904         var s = this.subs;
13905         var replaceIndex = -1;
13906         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13907             return s[++replaceIndex].buffer.join("");
13908         });
13909         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13910     },
13911
13912     apply : function(){
13913         return this.applyTemplate.apply(this, arguments);
13914     },
13915
13916     compile : function(){return this;}
13917 });
13918
13919 /**
13920  * Alias for fill().
13921  * @method
13922  */
13923 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13924  /**
13925  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13926  * var tpl = Roo.MasterTemplate.from('element-id');
13927  * @param {String/HTMLElement} el
13928  * @param {Object} config
13929  * @static
13930  */
13931 Roo.MasterTemplate.from = function(el, config){
13932     el = Roo.getDom(el);
13933     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13934 };/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945  
13946 /**
13947  * @class Roo.util.CSS
13948  * Utility class for manipulating CSS rules
13949  * @singleton
13950  */
13951 Roo.util.CSS = function(){
13952         var rules = null;
13953         var doc = document;
13954
13955     var camelRe = /(-[a-z])/gi;
13956     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13957
13958    return {
13959    /**
13960     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13961     * tag and appended to the HEAD of the document.
13962     * @param {String|Object} cssText The text containing the css rules
13963     * @param {String} id An id to add to the stylesheet for later removal
13964     * @return {StyleSheet}
13965     */
13966     createStyleSheet : function(cssText, id){
13967         var ss;
13968         var head = doc.getElementsByTagName("head")[0];
13969         var nrules = doc.createElement("style");
13970         nrules.setAttribute("type", "text/css");
13971         if(id){
13972             nrules.setAttribute("id", id);
13973         }
13974         if (typeof(cssText) != 'string') {
13975             // support object maps..
13976             // not sure if this a good idea.. 
13977             // perhaps it should be merged with the general css handling
13978             // and handle js style props.
13979             var cssTextNew = [];
13980             for(var n in cssText) {
13981                 var citems = [];
13982                 for(var k in cssText[n]) {
13983                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13984                 }
13985                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13986                 
13987             }
13988             cssText = cssTextNew.join("\n");
13989             
13990         }
13991        
13992        
13993        if(Roo.isIE){
13994            head.appendChild(nrules);
13995            ss = nrules.styleSheet;
13996            ss.cssText = cssText;
13997        }else{
13998            try{
13999                 nrules.appendChild(doc.createTextNode(cssText));
14000            }catch(e){
14001                nrules.cssText = cssText; 
14002            }
14003            head.appendChild(nrules);
14004            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14005        }
14006        this.cacheStyleSheet(ss);
14007        return ss;
14008    },
14009
14010    /**
14011     * Removes a style or link tag by id
14012     * @param {String} id The id of the tag
14013     */
14014    removeStyleSheet : function(id){
14015        var existing = doc.getElementById(id);
14016        if(existing){
14017            existing.parentNode.removeChild(existing);
14018        }
14019    },
14020
14021    /**
14022     * Dynamically swaps an existing stylesheet reference for a new one
14023     * @param {String} id The id of an existing link tag to remove
14024     * @param {String} url The href of the new stylesheet to include
14025     */
14026    swapStyleSheet : function(id, url){
14027        this.removeStyleSheet(id);
14028        var ss = doc.createElement("link");
14029        ss.setAttribute("rel", "stylesheet");
14030        ss.setAttribute("type", "text/css");
14031        ss.setAttribute("id", id);
14032        ss.setAttribute("href", url);
14033        doc.getElementsByTagName("head")[0].appendChild(ss);
14034    },
14035    
14036    /**
14037     * Refresh the rule cache if you have dynamically added stylesheets
14038     * @return {Object} An object (hash) of rules indexed by selector
14039     */
14040    refreshCache : function(){
14041        return this.getRules(true);
14042    },
14043
14044    // private
14045    cacheStyleSheet : function(stylesheet){
14046        if(!rules){
14047            rules = {};
14048        }
14049        try{// try catch for cross domain access issue
14050            var ssRules = stylesheet.cssRules || stylesheet.rules;
14051            for(var j = ssRules.length-1; j >= 0; --j){
14052                rules[ssRules[j].selectorText] = ssRules[j];
14053            }
14054        }catch(e){}
14055    },
14056    
14057    /**
14058     * Gets all css rules for the document
14059     * @param {Boolean} refreshCache true to refresh the internal cache
14060     * @return {Object} An object (hash) of rules indexed by selector
14061     */
14062    getRules : function(refreshCache){
14063                 if(rules == null || refreshCache){
14064                         rules = {};
14065                         var ds = doc.styleSheets;
14066                         for(var i =0, len = ds.length; i < len; i++){
14067                             try{
14068                         this.cacheStyleSheet(ds[i]);
14069                     }catch(e){} 
14070                 }
14071                 }
14072                 return rules;
14073         },
14074         
14075         /**
14076     * Gets an an individual CSS rule by selector(s)
14077     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14078     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14079     * @return {CSSRule} The CSS rule or null if one is not found
14080     */
14081    getRule : function(selector, refreshCache){
14082                 var rs = this.getRules(refreshCache);
14083                 if(!(selector instanceof Array)){
14084                     return rs[selector];
14085                 }
14086                 for(var i = 0; i < selector.length; i++){
14087                         if(rs[selector[i]]){
14088                                 return rs[selector[i]];
14089                         }
14090                 }
14091                 return null;
14092         },
14093         
14094         
14095         /**
14096     * Updates a rule property
14097     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14098     * @param {String} property The css property
14099     * @param {String} value The new value for the property
14100     * @return {Boolean} true If a rule was found and updated
14101     */
14102    updateRule : function(selector, property, value){
14103                 if(!(selector instanceof Array)){
14104                         var rule = this.getRule(selector);
14105                         if(rule){
14106                                 rule.style[property.replace(camelRe, camelFn)] = value;
14107                                 return true;
14108                         }
14109                 }else{
14110                         for(var i = 0; i < selector.length; i++){
14111                                 if(this.updateRule(selector[i], property, value)){
14112                                         return true;
14113                                 }
14114                         }
14115                 }
14116                 return false;
14117         }
14118    };   
14119 }();/*
14120  * Based on:
14121  * Ext JS Library 1.1.1
14122  * Copyright(c) 2006-2007, Ext JS, LLC.
14123  *
14124  * Originally Released Under LGPL - original licence link has changed is not relivant.
14125  *
14126  * Fork - LGPL
14127  * <script type="text/javascript">
14128  */
14129
14130  
14131
14132 /**
14133  * @class Roo.util.ClickRepeater
14134  * @extends Roo.util.Observable
14135  * 
14136  * A wrapper class which can be applied to any element. Fires a "click" event while the
14137  * mouse is pressed. The interval between firings may be specified in the config but
14138  * defaults to 10 milliseconds.
14139  * 
14140  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14141  * 
14142  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14143  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14144  * Similar to an autorepeat key delay.
14145  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14146  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14147  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14148  *           "interval" and "delay" are ignored. "immediate" is honored.
14149  * @cfg {Boolean} preventDefault True to prevent the default click event
14150  * @cfg {Boolean} stopDefault True to stop the default click event
14151  * 
14152  * @history
14153  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14154  *     2007-02-02 jvs Renamed to ClickRepeater
14155  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14156  *
14157  *  @constructor
14158  * @param {String/HTMLElement/Element} el The element to listen on
14159  * @param {Object} config
14160  **/
14161 Roo.util.ClickRepeater = function(el, config)
14162 {
14163     this.el = Roo.get(el);
14164     this.el.unselectable();
14165
14166     Roo.apply(this, config);
14167
14168     this.addEvents({
14169     /**
14170      * @event mousedown
14171      * Fires when the mouse button is depressed.
14172      * @param {Roo.util.ClickRepeater} this
14173      */
14174         "mousedown" : true,
14175     /**
14176      * @event click
14177      * Fires on a specified interval during the time the element is pressed.
14178      * @param {Roo.util.ClickRepeater} this
14179      */
14180         "click" : true,
14181     /**
14182      * @event mouseup
14183      * Fires when the mouse key is released.
14184      * @param {Roo.util.ClickRepeater} this
14185      */
14186         "mouseup" : true
14187     });
14188
14189     this.el.on("mousedown", this.handleMouseDown, this);
14190     if(this.preventDefault || this.stopDefault){
14191         this.el.on("click", function(e){
14192             if(this.preventDefault){
14193                 e.preventDefault();
14194             }
14195             if(this.stopDefault){
14196                 e.stopEvent();
14197             }
14198         }, this);
14199     }
14200
14201     // allow inline handler
14202     if(this.handler){
14203         this.on("click", this.handler,  this.scope || this);
14204     }
14205
14206     Roo.util.ClickRepeater.superclass.constructor.call(this);
14207 };
14208
14209 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14210     interval : 20,
14211     delay: 250,
14212     preventDefault : true,
14213     stopDefault : false,
14214     timer : 0,
14215
14216     // private
14217     handleMouseDown : function(){
14218         clearTimeout(this.timer);
14219         this.el.blur();
14220         if(this.pressClass){
14221             this.el.addClass(this.pressClass);
14222         }
14223         this.mousedownTime = new Date();
14224
14225         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14226         this.el.on("mouseout", this.handleMouseOut, this);
14227
14228         this.fireEvent("mousedown", this);
14229         this.fireEvent("click", this);
14230         
14231         this.timer = this.click.defer(this.delay || this.interval, this);
14232     },
14233
14234     // private
14235     click : function(){
14236         this.fireEvent("click", this);
14237         this.timer = this.click.defer(this.getInterval(), this);
14238     },
14239
14240     // private
14241     getInterval: function(){
14242         if(!this.accelerate){
14243             return this.interval;
14244         }
14245         var pressTime = this.mousedownTime.getElapsed();
14246         if(pressTime < 500){
14247             return 400;
14248         }else if(pressTime < 1700){
14249             return 320;
14250         }else if(pressTime < 2600){
14251             return 250;
14252         }else if(pressTime < 3500){
14253             return 180;
14254         }else if(pressTime < 4400){
14255             return 140;
14256         }else if(pressTime < 5300){
14257             return 80;
14258         }else if(pressTime < 6200){
14259             return 50;
14260         }else{
14261             return 10;
14262         }
14263     },
14264
14265     // private
14266     handleMouseOut : function(){
14267         clearTimeout(this.timer);
14268         if(this.pressClass){
14269             this.el.removeClass(this.pressClass);
14270         }
14271         this.el.on("mouseover", this.handleMouseReturn, this);
14272     },
14273
14274     // private
14275     handleMouseReturn : function(){
14276         this.el.un("mouseover", this.handleMouseReturn);
14277         if(this.pressClass){
14278             this.el.addClass(this.pressClass);
14279         }
14280         this.click();
14281     },
14282
14283     // private
14284     handleMouseUp : function(){
14285         clearTimeout(this.timer);
14286         this.el.un("mouseover", this.handleMouseReturn);
14287         this.el.un("mouseout", this.handleMouseOut);
14288         Roo.get(document).un("mouseup", this.handleMouseUp);
14289         this.el.removeClass(this.pressClass);
14290         this.fireEvent("mouseup", this);
14291     }
14292 });/*
14293  * Based on:
14294  * Ext JS Library 1.1.1
14295  * Copyright(c) 2006-2007, Ext JS, LLC.
14296  *
14297  * Originally Released Under LGPL - original licence link has changed is not relivant.
14298  *
14299  * Fork - LGPL
14300  * <script type="text/javascript">
14301  */
14302
14303  
14304 /**
14305  * @class Roo.KeyNav
14306  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14307  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14308  * way to implement custom navigation schemes for any UI component.</p>
14309  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14310  * pageUp, pageDown, del, home, end.  Usage:</p>
14311  <pre><code>
14312 var nav = new Roo.KeyNav("my-element", {
14313     "left" : function(e){
14314         this.moveLeft(e.ctrlKey);
14315     },
14316     "right" : function(e){
14317         this.moveRight(e.ctrlKey);
14318     },
14319     "enter" : function(e){
14320         this.save();
14321     },
14322     scope : this
14323 });
14324 </code></pre>
14325  * @constructor
14326  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14327  * @param {Object} config The config
14328  */
14329 Roo.KeyNav = function(el, config){
14330     this.el = Roo.get(el);
14331     Roo.apply(this, config);
14332     if(!this.disabled){
14333         this.disabled = true;
14334         this.enable();
14335     }
14336 };
14337
14338 Roo.KeyNav.prototype = {
14339     /**
14340      * @cfg {Boolean} disabled
14341      * True to disable this KeyNav instance (defaults to false)
14342      */
14343     disabled : false,
14344     /**
14345      * @cfg {String} defaultEventAction
14346      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14347      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14348      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14349      */
14350     defaultEventAction: "stopEvent",
14351     /**
14352      * @cfg {Boolean} forceKeyDown
14353      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14354      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14355      * handle keydown instead of keypress.
14356      */
14357     forceKeyDown : false,
14358
14359     // private
14360     prepareEvent : function(e){
14361         var k = e.getKey();
14362         var h = this.keyToHandler[k];
14363         //if(h && this[h]){
14364         //    e.stopPropagation();
14365         //}
14366         if(Roo.isSafari && h && k >= 37 && k <= 40){
14367             e.stopEvent();
14368         }
14369     },
14370
14371     // private
14372     relay : function(e){
14373         var k = e.getKey();
14374         var h = this.keyToHandler[k];
14375         if(h && this[h]){
14376             if(this.doRelay(e, this[h], h) !== true){
14377                 e[this.defaultEventAction]();
14378             }
14379         }
14380     },
14381
14382     // private
14383     doRelay : function(e, h, hname){
14384         return h.call(this.scope || this, e);
14385     },
14386
14387     // possible handlers
14388     enter : false,
14389     left : false,
14390     right : false,
14391     up : false,
14392     down : false,
14393     tab : false,
14394     esc : false,
14395     pageUp : false,
14396     pageDown : false,
14397     del : false,
14398     home : false,
14399     end : false,
14400
14401     // quick lookup hash
14402     keyToHandler : {
14403         37 : "left",
14404         39 : "right",
14405         38 : "up",
14406         40 : "down",
14407         33 : "pageUp",
14408         34 : "pageDown",
14409         46 : "del",
14410         36 : "home",
14411         35 : "end",
14412         13 : "enter",
14413         27 : "esc",
14414         9  : "tab"
14415     },
14416
14417         /**
14418          * Enable this KeyNav
14419          */
14420         enable: function(){
14421                 if(this.disabled){
14422             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14423             // the EventObject will normalize Safari automatically
14424             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14425                 this.el.on("keydown", this.relay,  this);
14426             }else{
14427                 this.el.on("keydown", this.prepareEvent,  this);
14428                 this.el.on("keypress", this.relay,  this);
14429             }
14430                     this.disabled = false;
14431                 }
14432         },
14433
14434         /**
14435          * Disable this KeyNav
14436          */
14437         disable: function(){
14438                 if(!this.disabled){
14439                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14440                 this.el.un("keydown", this.relay);
14441             }else{
14442                 this.el.un("keydown", this.prepareEvent);
14443                 this.el.un("keypress", this.relay);
14444             }
14445                     this.disabled = true;
14446                 }
14447         }
14448 };/*
14449  * Based on:
14450  * Ext JS Library 1.1.1
14451  * Copyright(c) 2006-2007, Ext JS, LLC.
14452  *
14453  * Originally Released Under LGPL - original licence link has changed is not relivant.
14454  *
14455  * Fork - LGPL
14456  * <script type="text/javascript">
14457  */
14458
14459  
14460 /**
14461  * @class Roo.KeyMap
14462  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14463  * The constructor accepts the same config object as defined by {@link #addBinding}.
14464  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14465  * combination it will call the function with this signature (if the match is a multi-key
14466  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14467  * A KeyMap can also handle a string representation of keys.<br />
14468  * Usage:
14469  <pre><code>
14470 // map one key by key code
14471 var map = new Roo.KeyMap("my-element", {
14472     key: 13, // or Roo.EventObject.ENTER
14473     fn: myHandler,
14474     scope: myObject
14475 });
14476
14477 // map multiple keys to one action by string
14478 var map = new Roo.KeyMap("my-element", {
14479     key: "a\r\n\t",
14480     fn: myHandler,
14481     scope: myObject
14482 });
14483
14484 // map multiple keys to multiple actions by strings and array of codes
14485 var map = new Roo.KeyMap("my-element", [
14486     {
14487         key: [10,13],
14488         fn: function(){ alert("Return was pressed"); }
14489     }, {
14490         key: "abc",
14491         fn: function(){ alert('a, b or c was pressed'); }
14492     }, {
14493         key: "\t",
14494         ctrl:true,
14495         shift:true,
14496         fn: function(){ alert('Control + shift + tab was pressed.'); }
14497     }
14498 ]);
14499 </code></pre>
14500  * <b>Note: A KeyMap starts enabled</b>
14501  * @constructor
14502  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14503  * @param {Object} config The config (see {@link #addBinding})
14504  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14505  */
14506 Roo.KeyMap = function(el, config, eventName){
14507     this.el  = Roo.get(el);
14508     this.eventName = eventName || "keydown";
14509     this.bindings = [];
14510     if(config){
14511         this.addBinding(config);
14512     }
14513     this.enable();
14514 };
14515
14516 Roo.KeyMap.prototype = {
14517     /**
14518      * True to stop the event from bubbling and prevent the default browser action if the
14519      * key was handled by the KeyMap (defaults to false)
14520      * @type Boolean
14521      */
14522     stopEvent : false,
14523
14524     /**
14525      * Add a new binding to this KeyMap. The following config object properties are supported:
14526      * <pre>
14527 Property    Type             Description
14528 ----------  ---------------  ----------------------------------------------------------------------
14529 key         String/Array     A single keycode or an array of keycodes to handle
14530 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14531 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14532 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14533 fn          Function         The function to call when KeyMap finds the expected key combination
14534 scope       Object           The scope of the callback function
14535 </pre>
14536      *
14537      * Usage:
14538      * <pre><code>
14539 // Create a KeyMap
14540 var map = new Roo.KeyMap(document, {
14541     key: Roo.EventObject.ENTER,
14542     fn: handleKey,
14543     scope: this
14544 });
14545
14546 //Add a new binding to the existing KeyMap later
14547 map.addBinding({
14548     key: 'abc',
14549     shift: true,
14550     fn: handleKey,
14551     scope: this
14552 });
14553 </code></pre>
14554      * @param {Object/Array} config A single KeyMap config or an array of configs
14555      */
14556         addBinding : function(config){
14557         if(config instanceof Array){
14558             for(var i = 0, len = config.length; i < len; i++){
14559                 this.addBinding(config[i]);
14560             }
14561             return;
14562         }
14563         var keyCode = config.key,
14564             shift = config.shift, 
14565             ctrl = config.ctrl, 
14566             alt = config.alt,
14567             fn = config.fn,
14568             scope = config.scope;
14569         if(typeof keyCode == "string"){
14570             var ks = [];
14571             var keyString = keyCode.toUpperCase();
14572             for(var j = 0, len = keyString.length; j < len; j++){
14573                 ks.push(keyString.charCodeAt(j));
14574             }
14575             keyCode = ks;
14576         }
14577         var keyArray = keyCode instanceof Array;
14578         var handler = function(e){
14579             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14580                 var k = e.getKey();
14581                 if(keyArray){
14582                     for(var i = 0, len = keyCode.length; i < len; i++){
14583                         if(keyCode[i] == k){
14584                           if(this.stopEvent){
14585                               e.stopEvent();
14586                           }
14587                           fn.call(scope || window, k, e);
14588                           return;
14589                         }
14590                     }
14591                 }else{
14592                     if(k == keyCode){
14593                         if(this.stopEvent){
14594                            e.stopEvent();
14595                         }
14596                         fn.call(scope || window, k, e);
14597                     }
14598                 }
14599             }
14600         };
14601         this.bindings.push(handler);  
14602         },
14603
14604     /**
14605      * Shorthand for adding a single key listener
14606      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14607      * following options:
14608      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14609      * @param {Function} fn The function to call
14610      * @param {Object} scope (optional) The scope of the function
14611      */
14612     on : function(key, fn, scope){
14613         var keyCode, shift, ctrl, alt;
14614         if(typeof key == "object" && !(key instanceof Array)){
14615             keyCode = key.key;
14616             shift = key.shift;
14617             ctrl = key.ctrl;
14618             alt = key.alt;
14619         }else{
14620             keyCode = key;
14621         }
14622         this.addBinding({
14623             key: keyCode,
14624             shift: shift,
14625             ctrl: ctrl,
14626             alt: alt,
14627             fn: fn,
14628             scope: scope
14629         })
14630     },
14631
14632     // private
14633     handleKeyDown : function(e){
14634             if(this.enabled){ //just in case
14635             var b = this.bindings;
14636             for(var i = 0, len = b.length; i < len; i++){
14637                 b[i].call(this, e);
14638             }
14639             }
14640         },
14641         
14642         /**
14643          * Returns true if this KeyMap is enabled
14644          * @return {Boolean} 
14645          */
14646         isEnabled : function(){
14647             return this.enabled;  
14648         },
14649         
14650         /**
14651          * Enables this KeyMap
14652          */
14653         enable: function(){
14654                 if(!this.enabled){
14655                     this.el.on(this.eventName, this.handleKeyDown, this);
14656                     this.enabled = true;
14657                 }
14658         },
14659
14660         /**
14661          * Disable this KeyMap
14662          */
14663         disable: function(){
14664                 if(this.enabled){
14665                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14666                     this.enabled = false;
14667                 }
14668         }
14669 };/*
14670  * Based on:
14671  * Ext JS Library 1.1.1
14672  * Copyright(c) 2006-2007, Ext JS, LLC.
14673  *
14674  * Originally Released Under LGPL - original licence link has changed is not relivant.
14675  *
14676  * Fork - LGPL
14677  * <script type="text/javascript">
14678  */
14679
14680  
14681 /**
14682  * @class Roo.util.TextMetrics
14683  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14684  * wide, in pixels, a given block of text will be.
14685  * @singleton
14686  */
14687 Roo.util.TextMetrics = function(){
14688     var shared;
14689     return {
14690         /**
14691          * Measures the size of the specified text
14692          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14693          * that can affect the size of the rendered text
14694          * @param {String} text The text to measure
14695          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14696          * in order to accurately measure the text height
14697          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14698          */
14699         measure : function(el, text, fixedWidth){
14700             if(!shared){
14701                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14702             }
14703             shared.bind(el);
14704             shared.setFixedWidth(fixedWidth || 'auto');
14705             return shared.getSize(text);
14706         },
14707
14708         /**
14709          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14710          * the overhead of multiple calls to initialize the style properties on each measurement.
14711          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14712          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14713          * in order to accurately measure the text height
14714          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14715          */
14716         createInstance : function(el, fixedWidth){
14717             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14718         }
14719     };
14720 }();
14721
14722  
14723
14724 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14725     var ml = new Roo.Element(document.createElement('div'));
14726     document.body.appendChild(ml.dom);
14727     ml.position('absolute');
14728     ml.setLeftTop(-1000, -1000);
14729     ml.hide();
14730
14731     if(fixedWidth){
14732         ml.setWidth(fixedWidth);
14733     }
14734      
14735     var instance = {
14736         /**
14737          * Returns the size of the specified text based on the internal element's style and width properties
14738          * @memberOf Roo.util.TextMetrics.Instance#
14739          * @param {String} text The text to measure
14740          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14741          */
14742         getSize : function(text){
14743             ml.update(text);
14744             var s = ml.getSize();
14745             ml.update('');
14746             return s;
14747         },
14748
14749         /**
14750          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14751          * that can affect the size of the rendered text
14752          * @memberOf Roo.util.TextMetrics.Instance#
14753          * @param {String/HTMLElement} el The element, dom node or id
14754          */
14755         bind : function(el){
14756             ml.setStyle(
14757                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14758             );
14759         },
14760
14761         /**
14762          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14763          * to set a fixed width in order to accurately measure the text height.
14764          * @memberOf Roo.util.TextMetrics.Instance#
14765          * @param {Number} width The width to set on the element
14766          */
14767         setFixedWidth : function(width){
14768             ml.setWidth(width);
14769         },
14770
14771         /**
14772          * Returns the measured width of the specified text
14773          * @memberOf Roo.util.TextMetrics.Instance#
14774          * @param {String} text The text to measure
14775          * @return {Number} width The width in pixels
14776          */
14777         getWidth : function(text){
14778             ml.dom.style.width = 'auto';
14779             return this.getSize(text).width;
14780         },
14781
14782         /**
14783          * Returns the measured height of the specified text.  For multiline text, be sure to call
14784          * {@link #setFixedWidth} if necessary.
14785          * @memberOf Roo.util.TextMetrics.Instance#
14786          * @param {String} text The text to measure
14787          * @return {Number} height The height in pixels
14788          */
14789         getHeight : function(text){
14790             return this.getSize(text).height;
14791         }
14792     };
14793
14794     instance.bind(bindTo);
14795
14796     return instance;
14797 };
14798
14799 // backwards compat
14800 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14801  * Based on:
14802  * Ext JS Library 1.1.1
14803  * Copyright(c) 2006-2007, Ext JS, LLC.
14804  *
14805  * Originally Released Under LGPL - original licence link has changed is not relivant.
14806  *
14807  * Fork - LGPL
14808  * <script type="text/javascript">
14809  */
14810
14811 /**
14812  * @class Roo.state.Provider
14813  * Abstract base class for state provider implementations. This class provides methods
14814  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14815  * Provider interface.
14816  */
14817 Roo.state.Provider = function(){
14818     /**
14819      * @event statechange
14820      * Fires when a state change occurs.
14821      * @param {Provider} this This state provider
14822      * @param {String} key The state key which was changed
14823      * @param {String} value The encoded value for the state
14824      */
14825     this.addEvents({
14826         "statechange": true
14827     });
14828     this.state = {};
14829     Roo.state.Provider.superclass.constructor.call(this);
14830 };
14831 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14832     /**
14833      * Returns the current value for a key
14834      * @param {String} name The key name
14835      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14836      * @return {Mixed} The state data
14837      */
14838     get : function(name, defaultValue){
14839         return typeof this.state[name] == "undefined" ?
14840             defaultValue : this.state[name];
14841     },
14842     
14843     /**
14844      * Clears a value from the state
14845      * @param {String} name The key name
14846      */
14847     clear : function(name){
14848         delete this.state[name];
14849         this.fireEvent("statechange", this, name, null);
14850     },
14851     
14852     /**
14853      * Sets the value for a key
14854      * @param {String} name The key name
14855      * @param {Mixed} value The value to set
14856      */
14857     set : function(name, value){
14858         this.state[name] = value;
14859         this.fireEvent("statechange", this, name, value);
14860     },
14861     
14862     /**
14863      * Decodes a string previously encoded with {@link #encodeValue}.
14864      * @param {String} value The value to decode
14865      * @return {Mixed} The decoded value
14866      */
14867     decodeValue : function(cookie){
14868         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14869         var matches = re.exec(unescape(cookie));
14870         if(!matches || !matches[1]) {
14871             return; // non state cookie
14872         }
14873         var type = matches[1];
14874         var v = matches[2];
14875         switch(type){
14876             case "n":
14877                 return parseFloat(v);
14878             case "d":
14879                 return new Date(Date.parse(v));
14880             case "b":
14881                 return (v == "1");
14882             case "a":
14883                 var all = [];
14884                 var values = v.split("^");
14885                 for(var i = 0, len = values.length; i < len; i++){
14886                     all.push(this.decodeValue(values[i]));
14887                 }
14888                 return all;
14889            case "o":
14890                 var all = {};
14891                 var values = v.split("^");
14892                 for(var i = 0, len = values.length; i < len; i++){
14893                     var kv = values[i].split("=");
14894                     all[kv[0]] = this.decodeValue(kv[1]);
14895                 }
14896                 return all;
14897            default:
14898                 return v;
14899         }
14900     },
14901     
14902     /**
14903      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14904      * @param {Mixed} value The value to encode
14905      * @return {String} The encoded value
14906      */
14907     encodeValue : function(v){
14908         var enc;
14909         if(typeof v == "number"){
14910             enc = "n:" + v;
14911         }else if(typeof v == "boolean"){
14912             enc = "b:" + (v ? "1" : "0");
14913         }else if(v instanceof Date){
14914             enc = "d:" + v.toGMTString();
14915         }else if(v instanceof Array){
14916             var flat = "";
14917             for(var i = 0, len = v.length; i < len; i++){
14918                 flat += this.encodeValue(v[i]);
14919                 if(i != len-1) {
14920                     flat += "^";
14921                 }
14922             }
14923             enc = "a:" + flat;
14924         }else if(typeof v == "object"){
14925             var flat = "";
14926             for(var key in v){
14927                 if(typeof v[key] != "function"){
14928                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14929                 }
14930             }
14931             enc = "o:" + flat.substring(0, flat.length-1);
14932         }else{
14933             enc = "s:" + v;
14934         }
14935         return escape(enc);        
14936     }
14937 });
14938
14939 /*
14940  * Based on:
14941  * Ext JS Library 1.1.1
14942  * Copyright(c) 2006-2007, Ext JS, LLC.
14943  *
14944  * Originally Released Under LGPL - original licence link has changed is not relivant.
14945  *
14946  * Fork - LGPL
14947  * <script type="text/javascript">
14948  */
14949 /**
14950  * @class Roo.state.Manager
14951  * This is the global state manager. By default all components that are "state aware" check this class
14952  * for state information if you don't pass them a custom state provider. In order for this class
14953  * to be useful, it must be initialized with a provider when your application initializes.
14954  <pre><code>
14955 // in your initialization function
14956 init : function(){
14957    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14958    ...
14959    // supposed you have a {@link Roo.BorderLayout}
14960    var layout = new Roo.BorderLayout(...);
14961    layout.restoreState();
14962    // or a {Roo.BasicDialog}
14963    var dialog = new Roo.BasicDialog(...);
14964    dialog.restoreState();
14965  </code></pre>
14966  * @singleton
14967  */
14968 Roo.state.Manager = function(){
14969     var provider = new Roo.state.Provider();
14970     
14971     return {
14972         /**
14973          * Configures the default state provider for your application
14974          * @param {Provider} stateProvider The state provider to set
14975          */
14976         setProvider : function(stateProvider){
14977             provider = stateProvider;
14978         },
14979         
14980         /**
14981          * Returns the current value for a key
14982          * @param {String} name The key name
14983          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14984          * @return {Mixed} The state data
14985          */
14986         get : function(key, defaultValue){
14987             return provider.get(key, defaultValue);
14988         },
14989         
14990         /**
14991          * Sets the value for a key
14992          * @param {String} name The key name
14993          * @param {Mixed} value The state data
14994          */
14995          set : function(key, value){
14996             provider.set(key, value);
14997         },
14998         
14999         /**
15000          * Clears a value from the state
15001          * @param {String} name The key name
15002          */
15003         clear : function(key){
15004             provider.clear(key);
15005         },
15006         
15007         /**
15008          * Gets the currently configured state provider
15009          * @return {Provider} The state provider
15010          */
15011         getProvider : function(){
15012             return provider;
15013         }
15014     };
15015 }();
15016 /*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026 /**
15027  * @class Roo.state.CookieProvider
15028  * @extends Roo.state.Provider
15029  * The default Provider implementation which saves state via cookies.
15030  * <br />Usage:
15031  <pre><code>
15032    var cp = new Roo.state.CookieProvider({
15033        path: "/cgi-bin/",
15034        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15035        domain: "roojs.com"
15036    })
15037    Roo.state.Manager.setProvider(cp);
15038  </code></pre>
15039  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15040  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15041  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15042  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15043  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15044  * domain the page is running on including the 'www' like 'www.roojs.com')
15045  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15046  * @constructor
15047  * Create a new CookieProvider
15048  * @param {Object} config The configuration object
15049  */
15050 Roo.state.CookieProvider = function(config){
15051     Roo.state.CookieProvider.superclass.constructor.call(this);
15052     this.path = "/";
15053     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15054     this.domain = null;
15055     this.secure = false;
15056     Roo.apply(this, config);
15057     this.state = this.readCookies();
15058 };
15059
15060 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15061     // private
15062     set : function(name, value){
15063         if(typeof value == "undefined" || value === null){
15064             this.clear(name);
15065             return;
15066         }
15067         this.setCookie(name, value);
15068         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15069     },
15070
15071     // private
15072     clear : function(name){
15073         this.clearCookie(name);
15074         Roo.state.CookieProvider.superclass.clear.call(this, name);
15075     },
15076
15077     // private
15078     readCookies : function(){
15079         var cookies = {};
15080         var c = document.cookie + ";";
15081         var re = /\s?(.*?)=(.*?);/g;
15082         var matches;
15083         while((matches = re.exec(c)) != null){
15084             var name = matches[1];
15085             var value = matches[2];
15086             if(name && name.substring(0,3) == "ys-"){
15087                 cookies[name.substr(3)] = this.decodeValue(value);
15088             }
15089         }
15090         return cookies;
15091     },
15092
15093     // private
15094     setCookie : function(name, value){
15095         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15096            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15097            ((this.path == null) ? "" : ("; path=" + this.path)) +
15098            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15099            ((this.secure == true) ? "; secure" : "");
15100     },
15101
15102     // private
15103     clearCookie : function(name){
15104         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15105            ((this.path == null) ? "" : ("; path=" + this.path)) +
15106            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15107            ((this.secure == true) ? "; secure" : "");
15108     }
15109 });/*
15110  * Based on:
15111  * Ext JS Library 1.1.1
15112  * Copyright(c) 2006-2007, Ext JS, LLC.
15113  *
15114  * Originally Released Under LGPL - original licence link has changed is not relivant.
15115  *
15116  * Fork - LGPL
15117  * <script type="text/javascript">
15118  */
15119  
15120
15121 /**
15122  * @class Roo.ComponentMgr
15123  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15124  * @singleton
15125  */
15126 Roo.ComponentMgr = function(){
15127     var all = new Roo.util.MixedCollection();
15128
15129     return {
15130         /**
15131          * Registers a component.
15132          * @param {Roo.Component} c The component
15133          */
15134         register : function(c){
15135             all.add(c);
15136         },
15137
15138         /**
15139          * Unregisters a component.
15140          * @param {Roo.Component} c The component
15141          */
15142         unregister : function(c){
15143             all.remove(c);
15144         },
15145
15146         /**
15147          * Returns a component by id
15148          * @param {String} id The component id
15149          */
15150         get : function(id){
15151             return all.get(id);
15152         },
15153
15154         /**
15155          * Registers a function that will be called when a specified component is added to ComponentMgr
15156          * @param {String} id The component id
15157          * @param {Funtction} fn The callback function
15158          * @param {Object} scope The scope of the callback
15159          */
15160         onAvailable : function(id, fn, scope){
15161             all.on("add", function(index, o){
15162                 if(o.id == id){
15163                     fn.call(scope || o, o);
15164                     all.un("add", fn, scope);
15165                 }
15166             });
15167         }
15168     };
15169 }();/*
15170  * Based on:
15171  * Ext JS Library 1.1.1
15172  * Copyright(c) 2006-2007, Ext JS, LLC.
15173  *
15174  * Originally Released Under LGPL - original licence link has changed is not relivant.
15175  *
15176  * Fork - LGPL
15177  * <script type="text/javascript">
15178  */
15179  
15180 /**
15181  * @class Roo.Component
15182  * @extends Roo.util.Observable
15183  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15184  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15185  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15186  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15187  * All visual components (widgets) that require rendering into a layout should subclass Component.
15188  * @constructor
15189  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15190  * 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
15191  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15192  */
15193 Roo.Component = function(config){
15194     config = config || {};
15195     if(config.tagName || config.dom || typeof config == "string"){ // element object
15196         config = {el: config, id: config.id || config};
15197     }
15198     this.initialConfig = config;
15199
15200     Roo.apply(this, config);
15201     this.addEvents({
15202         /**
15203          * @event disable
15204          * Fires after the component is disabled.
15205              * @param {Roo.Component} this
15206              */
15207         disable : true,
15208         /**
15209          * @event enable
15210          * Fires after the component is enabled.
15211              * @param {Roo.Component} this
15212              */
15213         enable : true,
15214         /**
15215          * @event beforeshow
15216          * Fires before the component is shown.  Return false to stop the show.
15217              * @param {Roo.Component} this
15218              */
15219         beforeshow : true,
15220         /**
15221          * @event show
15222          * Fires after the component is shown.
15223              * @param {Roo.Component} this
15224              */
15225         show : true,
15226         /**
15227          * @event beforehide
15228          * Fires before the component is hidden. Return false to stop the hide.
15229              * @param {Roo.Component} this
15230              */
15231         beforehide : true,
15232         /**
15233          * @event hide
15234          * Fires after the component is hidden.
15235              * @param {Roo.Component} this
15236              */
15237         hide : true,
15238         /**
15239          * @event beforerender
15240          * Fires before the component is rendered. Return false to stop the render.
15241              * @param {Roo.Component} this
15242              */
15243         beforerender : true,
15244         /**
15245          * @event render
15246          * Fires after the component is rendered.
15247              * @param {Roo.Component} this
15248              */
15249         render : true,
15250         /**
15251          * @event beforedestroy
15252          * Fires before the component is destroyed. Return false to stop the destroy.
15253              * @param {Roo.Component} this
15254              */
15255         beforedestroy : true,
15256         /**
15257          * @event destroy
15258          * Fires after the component is destroyed.
15259              * @param {Roo.Component} this
15260              */
15261         destroy : true
15262     });
15263     if(!this.id){
15264         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15265     }
15266     Roo.ComponentMgr.register(this);
15267     Roo.Component.superclass.constructor.call(this);
15268     this.initComponent();
15269     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15270         this.render(this.renderTo);
15271         delete this.renderTo;
15272     }
15273 };
15274
15275 /** @private */
15276 Roo.Component.AUTO_ID = 1000;
15277
15278 Roo.extend(Roo.Component, Roo.util.Observable, {
15279     /**
15280      * @scope Roo.Component.prototype
15281      * @type {Boolean}
15282      * true if this component is hidden. Read-only.
15283      */
15284     hidden : false,
15285     /**
15286      * @type {Boolean}
15287      * true if this component is disabled. Read-only.
15288      */
15289     disabled : false,
15290     /**
15291      * @type {Boolean}
15292      * true if this component has been rendered. Read-only.
15293      */
15294     rendered : false,
15295     
15296     /** @cfg {String} disableClass
15297      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15298      */
15299     disabledClass : "x-item-disabled",
15300         /** @cfg {Boolean} allowDomMove
15301          * Whether the component can move the Dom node when rendering (defaults to true).
15302          */
15303     allowDomMove : true,
15304     /** @cfg {String} hideMode (display|visibility)
15305      * How this component should hidden. Supported values are
15306      * "visibility" (css visibility), "offsets" (negative offset position) and
15307      * "display" (css display) - defaults to "display".
15308      */
15309     hideMode: 'display',
15310
15311     /** @private */
15312     ctype : "Roo.Component",
15313
15314     /**
15315      * @cfg {String} actionMode 
15316      * which property holds the element that used for  hide() / show() / disable() / enable()
15317      * default is 'el' 
15318      */
15319     actionMode : "el",
15320
15321     /** @private */
15322     getActionEl : function(){
15323         return this[this.actionMode];
15324     },
15325
15326     initComponent : Roo.emptyFn,
15327     /**
15328      * If this is a lazy rendering component, render it to its container element.
15329      * @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.
15330      */
15331     render : function(container, position){
15332         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15333             if(!container && this.el){
15334                 this.el = Roo.get(this.el);
15335                 container = this.el.dom.parentNode;
15336                 this.allowDomMove = false;
15337             }
15338             this.container = Roo.get(container);
15339             this.rendered = true;
15340             if(position !== undefined){
15341                 if(typeof position == 'number'){
15342                     position = this.container.dom.childNodes[position];
15343                 }else{
15344                     position = Roo.getDom(position);
15345                 }
15346             }
15347             this.onRender(this.container, position || null);
15348             if(this.cls){
15349                 this.el.addClass(this.cls);
15350                 delete this.cls;
15351             }
15352             if(this.style){
15353                 this.el.applyStyles(this.style);
15354                 delete this.style;
15355             }
15356             this.fireEvent("render", this);
15357             this.afterRender(this.container);
15358             if(this.hidden){
15359                 this.hide();
15360             }
15361             if(this.disabled){
15362                 this.disable();
15363             }
15364         }
15365         return this;
15366     },
15367
15368     /** @private */
15369     // default function is not really useful
15370     onRender : function(ct, position){
15371         if(this.el){
15372             this.el = Roo.get(this.el);
15373             if(this.allowDomMove !== false){
15374                 ct.dom.insertBefore(this.el.dom, position);
15375             }
15376         }
15377     },
15378
15379     /** @private */
15380     getAutoCreate : function(){
15381         var cfg = typeof this.autoCreate == "object" ?
15382                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15383         if(this.id && !cfg.id){
15384             cfg.id = this.id;
15385         }
15386         return cfg;
15387     },
15388
15389     /** @private */
15390     afterRender : Roo.emptyFn,
15391
15392     /**
15393      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15394      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15395      */
15396     destroy : function(){
15397         if(this.fireEvent("beforedestroy", this) !== false){
15398             this.purgeListeners();
15399             this.beforeDestroy();
15400             if(this.rendered){
15401                 this.el.removeAllListeners();
15402                 this.el.remove();
15403                 if(this.actionMode == "container"){
15404                     this.container.remove();
15405                 }
15406             }
15407             this.onDestroy();
15408             Roo.ComponentMgr.unregister(this);
15409             this.fireEvent("destroy", this);
15410         }
15411     },
15412
15413         /** @private */
15414     beforeDestroy : function(){
15415
15416     },
15417
15418         /** @private */
15419         onDestroy : function(){
15420
15421     },
15422
15423     /**
15424      * Returns the underlying {@link Roo.Element}.
15425      * @return {Roo.Element} The element
15426      */
15427     getEl : function(){
15428         return this.el;
15429     },
15430
15431     /**
15432      * Returns the id of this component.
15433      * @return {String}
15434      */
15435     getId : function(){
15436         return this.id;
15437     },
15438
15439     /**
15440      * Try to focus this component.
15441      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15442      * @return {Roo.Component} this
15443      */
15444     focus : function(selectText){
15445         if(this.rendered){
15446             this.el.focus();
15447             if(selectText === true){
15448                 this.el.dom.select();
15449             }
15450         }
15451         return this;
15452     },
15453
15454     /** @private */
15455     blur : function(){
15456         if(this.rendered){
15457             this.el.blur();
15458         }
15459         return this;
15460     },
15461
15462     /**
15463      * Disable this component.
15464      * @return {Roo.Component} this
15465      */
15466     disable : function(){
15467         if(this.rendered){
15468             this.onDisable();
15469         }
15470         this.disabled = true;
15471         this.fireEvent("disable", this);
15472         return this;
15473     },
15474
15475         // private
15476     onDisable : function(){
15477         this.getActionEl().addClass(this.disabledClass);
15478         this.el.dom.disabled = true;
15479     },
15480
15481     /**
15482      * Enable this component.
15483      * @return {Roo.Component} this
15484      */
15485     enable : function(){
15486         if(this.rendered){
15487             this.onEnable();
15488         }
15489         this.disabled = false;
15490         this.fireEvent("enable", this);
15491         return this;
15492     },
15493
15494         // private
15495     onEnable : function(){
15496         this.getActionEl().removeClass(this.disabledClass);
15497         this.el.dom.disabled = false;
15498     },
15499
15500     /**
15501      * Convenience function for setting disabled/enabled by boolean.
15502      * @param {Boolean} disabled
15503      */
15504     setDisabled : function(disabled){
15505         this[disabled ? "disable" : "enable"]();
15506     },
15507
15508     /**
15509      * Show this component.
15510      * @return {Roo.Component} this
15511      */
15512     show: function(){
15513         if(this.fireEvent("beforeshow", this) !== false){
15514             this.hidden = false;
15515             if(this.rendered){
15516                 this.onShow();
15517             }
15518             this.fireEvent("show", this);
15519         }
15520         return this;
15521     },
15522
15523     // private
15524     onShow : function(){
15525         var ae = this.getActionEl();
15526         if(this.hideMode == 'visibility'){
15527             ae.dom.style.visibility = "visible";
15528         }else if(this.hideMode == 'offsets'){
15529             ae.removeClass('x-hidden');
15530         }else{
15531             ae.dom.style.display = "";
15532         }
15533     },
15534
15535     /**
15536      * Hide this component.
15537      * @return {Roo.Component} this
15538      */
15539     hide: function(){
15540         if(this.fireEvent("beforehide", this) !== false){
15541             this.hidden = true;
15542             if(this.rendered){
15543                 this.onHide();
15544             }
15545             this.fireEvent("hide", this);
15546         }
15547         return this;
15548     },
15549
15550     // private
15551     onHide : function(){
15552         var ae = this.getActionEl();
15553         if(this.hideMode == 'visibility'){
15554             ae.dom.style.visibility = "hidden";
15555         }else if(this.hideMode == 'offsets'){
15556             ae.addClass('x-hidden');
15557         }else{
15558             ae.dom.style.display = "none";
15559         }
15560     },
15561
15562     /**
15563      * Convenience function to hide or show this component by boolean.
15564      * @param {Boolean} visible True to show, false to hide
15565      * @return {Roo.Component} this
15566      */
15567     setVisible: function(visible){
15568         if(visible) {
15569             this.show();
15570         }else{
15571             this.hide();
15572         }
15573         return this;
15574     },
15575
15576     /**
15577      * Returns true if this component is visible.
15578      */
15579     isVisible : function(){
15580         return this.getActionEl().isVisible();
15581     },
15582
15583     cloneConfig : function(overrides){
15584         overrides = overrides || {};
15585         var id = overrides.id || Roo.id();
15586         var cfg = Roo.applyIf(overrides, this.initialConfig);
15587         cfg.id = id; // prevent dup id
15588         return new this.constructor(cfg);
15589     }
15590 });/*
15591  * Based on:
15592  * Ext JS Library 1.1.1
15593  * Copyright(c) 2006-2007, Ext JS, LLC.
15594  *
15595  * Originally Released Under LGPL - original licence link has changed is not relivant.
15596  *
15597  * Fork - LGPL
15598  * <script type="text/javascript">
15599  */
15600
15601 /**
15602  * @class Roo.BoxComponent
15603  * @extends Roo.Component
15604  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15605  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15606  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15607  * layout containers.
15608  * @constructor
15609  * @param {Roo.Element/String/Object} config The configuration options.
15610  */
15611 Roo.BoxComponent = function(config){
15612     Roo.Component.call(this, config);
15613     this.addEvents({
15614         /**
15615          * @event resize
15616          * Fires after the component is resized.
15617              * @param {Roo.Component} this
15618              * @param {Number} adjWidth The box-adjusted width that was set
15619              * @param {Number} adjHeight The box-adjusted height that was set
15620              * @param {Number} rawWidth The width that was originally specified
15621              * @param {Number} rawHeight The height that was originally specified
15622              */
15623         resize : true,
15624         /**
15625          * @event move
15626          * Fires after the component is moved.
15627              * @param {Roo.Component} this
15628              * @param {Number} x The new x position
15629              * @param {Number} y The new y position
15630              */
15631         move : true
15632     });
15633 };
15634
15635 Roo.extend(Roo.BoxComponent, Roo.Component, {
15636     // private, set in afterRender to signify that the component has been rendered
15637     boxReady : false,
15638     // private, used to defer height settings to subclasses
15639     deferHeight: false,
15640     /** @cfg {Number} width
15641      * width (optional) size of component
15642      */
15643      /** @cfg {Number} height
15644      * height (optional) size of component
15645      */
15646      
15647     /**
15648      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15649      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15650      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15651      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15652      * @return {Roo.BoxComponent} this
15653      */
15654     setSize : function(w, h){
15655         // support for standard size objects
15656         if(typeof w == 'object'){
15657             h = w.height;
15658             w = w.width;
15659         }
15660         // not rendered
15661         if(!this.boxReady){
15662             this.width = w;
15663             this.height = h;
15664             return this;
15665         }
15666
15667         // prevent recalcs when not needed
15668         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15669             return this;
15670         }
15671         this.lastSize = {width: w, height: h};
15672
15673         var adj = this.adjustSize(w, h);
15674         var aw = adj.width, ah = adj.height;
15675         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15676             var rz = this.getResizeEl();
15677             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15678                 rz.setSize(aw, ah);
15679             }else if(!this.deferHeight && ah !== undefined){
15680                 rz.setHeight(ah);
15681             }else if(aw !== undefined){
15682                 rz.setWidth(aw);
15683             }
15684             this.onResize(aw, ah, w, h);
15685             this.fireEvent('resize', this, aw, ah, w, h);
15686         }
15687         return this;
15688     },
15689
15690     /**
15691      * Gets the current size of the component's underlying element.
15692      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15693      */
15694     getSize : function(){
15695         return this.el.getSize();
15696     },
15697
15698     /**
15699      * Gets the current XY position of the component's underlying element.
15700      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15701      * @return {Array} The XY position of the element (e.g., [100, 200])
15702      */
15703     getPosition : function(local){
15704         if(local === true){
15705             return [this.el.getLeft(true), this.el.getTop(true)];
15706         }
15707         return this.xy || this.el.getXY();
15708     },
15709
15710     /**
15711      * Gets the current box measurements of the component's underlying element.
15712      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15713      * @returns {Object} box An object in the format {x, y, width, height}
15714      */
15715     getBox : function(local){
15716         var s = this.el.getSize();
15717         if(local){
15718             s.x = this.el.getLeft(true);
15719             s.y = this.el.getTop(true);
15720         }else{
15721             var xy = this.xy || this.el.getXY();
15722             s.x = xy[0];
15723             s.y = xy[1];
15724         }
15725         return s;
15726     },
15727
15728     /**
15729      * Sets the current box measurements of the component's underlying element.
15730      * @param {Object} box An object in the format {x, y, width, height}
15731      * @returns {Roo.BoxComponent} this
15732      */
15733     updateBox : function(box){
15734         this.setSize(box.width, box.height);
15735         this.setPagePosition(box.x, box.y);
15736         return this;
15737     },
15738
15739     // protected
15740     getResizeEl : function(){
15741         return this.resizeEl || this.el;
15742     },
15743
15744     // protected
15745     getPositionEl : function(){
15746         return this.positionEl || this.el;
15747     },
15748
15749     /**
15750      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15751      * This method fires the move event.
15752      * @param {Number} left The new left
15753      * @param {Number} top The new top
15754      * @returns {Roo.BoxComponent} this
15755      */
15756     setPosition : function(x, y){
15757         this.x = x;
15758         this.y = y;
15759         if(!this.boxReady){
15760             return this;
15761         }
15762         var adj = this.adjustPosition(x, y);
15763         var ax = adj.x, ay = adj.y;
15764
15765         var el = this.getPositionEl();
15766         if(ax !== undefined || ay !== undefined){
15767             if(ax !== undefined && ay !== undefined){
15768                 el.setLeftTop(ax, ay);
15769             }else if(ax !== undefined){
15770                 el.setLeft(ax);
15771             }else if(ay !== undefined){
15772                 el.setTop(ay);
15773             }
15774             this.onPosition(ax, ay);
15775             this.fireEvent('move', this, ax, ay);
15776         }
15777         return this;
15778     },
15779
15780     /**
15781      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15782      * This method fires the move event.
15783      * @param {Number} x The new x position
15784      * @param {Number} y The new y position
15785      * @returns {Roo.BoxComponent} this
15786      */
15787     setPagePosition : function(x, y){
15788         this.pageX = x;
15789         this.pageY = y;
15790         if(!this.boxReady){
15791             return;
15792         }
15793         if(x === undefined || y === undefined){ // cannot translate undefined points
15794             return;
15795         }
15796         var p = this.el.translatePoints(x, y);
15797         this.setPosition(p.left, p.top);
15798         return this;
15799     },
15800
15801     // private
15802     onRender : function(ct, position){
15803         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15804         if(this.resizeEl){
15805             this.resizeEl = Roo.get(this.resizeEl);
15806         }
15807         if(this.positionEl){
15808             this.positionEl = Roo.get(this.positionEl);
15809         }
15810     },
15811
15812     // private
15813     afterRender : function(){
15814         Roo.BoxComponent.superclass.afterRender.call(this);
15815         this.boxReady = true;
15816         this.setSize(this.width, this.height);
15817         if(this.x || this.y){
15818             this.setPosition(this.x, this.y);
15819         }
15820         if(this.pageX || this.pageY){
15821             this.setPagePosition(this.pageX, this.pageY);
15822         }
15823     },
15824
15825     /**
15826      * Force the component's size to recalculate based on the underlying element's current height and width.
15827      * @returns {Roo.BoxComponent} this
15828      */
15829     syncSize : function(){
15830         delete this.lastSize;
15831         this.setSize(this.el.getWidth(), this.el.getHeight());
15832         return this;
15833     },
15834
15835     /**
15836      * Called after the component is resized, this method is empty by default but can be implemented by any
15837      * subclass that needs to perform custom logic after a resize occurs.
15838      * @param {Number} adjWidth The box-adjusted width that was set
15839      * @param {Number} adjHeight The box-adjusted height that was set
15840      * @param {Number} rawWidth The width that was originally specified
15841      * @param {Number} rawHeight The height that was originally specified
15842      */
15843     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15844
15845     },
15846
15847     /**
15848      * Called after the component is moved, this method is empty by default but can be implemented by any
15849      * subclass that needs to perform custom logic after a move occurs.
15850      * @param {Number} x The new x position
15851      * @param {Number} y The new y position
15852      */
15853     onPosition : function(x, y){
15854
15855     },
15856
15857     // private
15858     adjustSize : function(w, h){
15859         if(this.autoWidth){
15860             w = 'auto';
15861         }
15862         if(this.autoHeight){
15863             h = 'auto';
15864         }
15865         return {width : w, height: h};
15866     },
15867
15868     // private
15869     adjustPosition : function(x, y){
15870         return {x : x, y: y};
15871     }
15872 });/*
15873  * Original code for Roojs - LGPL
15874  * <script type="text/javascript">
15875  */
15876  
15877 /**
15878  * @class Roo.XComponent
15879  * A delayed Element creator...
15880  * Or a way to group chunks of interface together.
15881  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15882  *  used in conjunction with XComponent.build() it will create an instance of each element,
15883  *  then call addxtype() to build the User interface.
15884  * 
15885  * Mypart.xyx = new Roo.XComponent({
15886
15887     parent : 'Mypart.xyz', // empty == document.element.!!
15888     order : '001',
15889     name : 'xxxx'
15890     region : 'xxxx'
15891     disabled : function() {} 
15892      
15893     tree : function() { // return an tree of xtype declared components
15894         var MODULE = this;
15895         return 
15896         {
15897             xtype : 'NestedLayoutPanel',
15898             // technicall
15899         }
15900      ]
15901  *})
15902  *
15903  *
15904  * It can be used to build a big heiracy, with parent etc.
15905  * or you can just use this to render a single compoent to a dom element
15906  * MYPART.render(Roo.Element | String(id) | dom_element )
15907  *
15908  *
15909  * Usage patterns.
15910  *
15911  * Classic Roo
15912  *
15913  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15914  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15915  *
15916  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15917  *
15918  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15919  * - if mulitple topModules exist, the last one is defined as the top module.
15920  *
15921  * Embeded Roo
15922  * 
15923  * When the top level or multiple modules are to embedded into a existing HTML page,
15924  * the parent element can container '#id' of the element where the module will be drawn.
15925  *
15926  * Bootstrap Roo
15927  *
15928  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15929  * it relies more on a include mechanism, where sub modules are included into an outer page.
15930  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15931  * 
15932  * Bootstrap Roo Included elements
15933  *
15934  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15935  * hence confusing the component builder as it thinks there are multiple top level elements. 
15936  *
15937  * 
15938  * 
15939  * @extends Roo.util.Observable
15940  * @constructor
15941  * @param cfg {Object} configuration of component
15942  * 
15943  */
15944 Roo.XComponent = function(cfg) {
15945     Roo.apply(this, cfg);
15946     this.addEvents({ 
15947         /**
15948              * @event built
15949              * Fires when this the componnt is built
15950              * @param {Roo.XComponent} c the component
15951              */
15952         'built' : true
15953         
15954     });
15955     this.region = this.region || 'center'; // default..
15956     Roo.XComponent.register(this);
15957     this.modules = false;
15958     this.el = false; // where the layout goes..
15959     
15960     
15961 }
15962 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15963     /**
15964      * @property el
15965      * The created element (with Roo.factory())
15966      * @type {Roo.Layout}
15967      */
15968     el  : false,
15969     
15970     /**
15971      * @property el
15972      * for BC  - use el in new code
15973      * @type {Roo.Layout}
15974      */
15975     panel : false,
15976     
15977     /**
15978      * @property layout
15979      * for BC  - use el in new code
15980      * @type {Roo.Layout}
15981      */
15982     layout : false,
15983     
15984      /**
15985      * @cfg {Function|boolean} disabled
15986      * If this module is disabled by some rule, return true from the funtion
15987      */
15988     disabled : false,
15989     
15990     /**
15991      * @cfg {String} parent 
15992      * Name of parent element which it get xtype added to..
15993      */
15994     parent: false,
15995     
15996     /**
15997      * @cfg {String} order
15998      * Used to set the order in which elements are created (usefull for multiple tabs)
15999      */
16000     
16001     order : false,
16002     /**
16003      * @cfg {String} name
16004      * String to display while loading.
16005      */
16006     name : false,
16007     /**
16008      * @cfg {String} region
16009      * Region to render component to (defaults to center)
16010      */
16011     region : 'center',
16012     
16013     /**
16014      * @cfg {Array} items
16015      * A single item array - the first element is the root of the tree..
16016      * It's done this way to stay compatible with the Xtype system...
16017      */
16018     items : false,
16019     
16020     /**
16021      * @property _tree
16022      * The method that retuns the tree of parts that make up this compoennt 
16023      * @type {function}
16024      */
16025     _tree  : false,
16026     
16027      /**
16028      * render
16029      * render element to dom or tree
16030      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16031      */
16032     
16033     render : function(el)
16034     {
16035         
16036         el = el || false;
16037         var hp = this.parent ? 1 : 0;
16038         Roo.debug &&  Roo.log(this);
16039         
16040         var tree = this._tree ? this._tree() : this.tree();
16041
16042         
16043         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16044             // if parent is a '#.....' string, then let's use that..
16045             var ename = this.parent.substr(1);
16046             this.parent = false;
16047             Roo.debug && Roo.log(ename);
16048             switch (ename) {
16049                 case 'bootstrap-body':
16050                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16051                         // this is the BorderLayout standard?
16052                        this.parent = { el : true };
16053                        break;
16054                     }
16055                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16056                         // need to insert stuff...
16057                         this.parent =  {
16058                              el : new Roo.bootstrap.layout.Border({
16059                                  el : document.body, 
16060                      
16061                                  center: {
16062                                     titlebar: false,
16063                                     autoScroll:false,
16064                                     closeOnTab: true,
16065                                     tabPosition: 'top',
16066                                       //resizeTabs: true,
16067                                     alwaysShowTabs: true,
16068                                     hideTabs: false
16069                                      //minTabWidth: 140
16070                                  }
16071                              })
16072                         
16073                          };
16074                          break;
16075                     }
16076                          
16077                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16078                         this.parent = { el :  new  Roo.bootstrap.Body() };
16079                         Roo.debug && Roo.log("setting el to doc body");
16080                          
16081                     } else {
16082                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16083                     }
16084                     break;
16085                 case 'bootstrap':
16086                     this.parent = { el : true};
16087                     // fall through
16088                 default:
16089                     el = Roo.get(ename);
16090                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16091                         this.parent = { el : true};
16092                     }
16093                     
16094                     break;
16095             }
16096                 
16097             
16098             if (!el && !this.parent) {
16099                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16100                 return;
16101             }
16102         }
16103         
16104         Roo.debug && Roo.log("EL:");
16105         Roo.debug && Roo.log(el);
16106         Roo.debug && Roo.log("this.parent.el:");
16107         Roo.debug && Roo.log(this.parent.el);
16108         
16109
16110         // altertive root elements ??? - we need a better way to indicate these.
16111         var is_alt = Roo.XComponent.is_alt ||
16112                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16113                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16114                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16115         
16116         
16117         
16118         if (!this.parent && is_alt) {
16119             //el = Roo.get(document.body);
16120             this.parent = { el : true };
16121         }
16122             
16123             
16124         
16125         if (!this.parent) {
16126             
16127             Roo.debug && Roo.log("no parent - creating one");
16128             
16129             el = el ? Roo.get(el) : false;      
16130             
16131             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16132                 
16133                 this.parent =  {
16134                     el : new Roo.bootstrap.layout.Border({
16135                         el: el || document.body,
16136                     
16137                         center: {
16138                             titlebar: false,
16139                             autoScroll:false,
16140                             closeOnTab: true,
16141                             tabPosition: 'top',
16142                              //resizeTabs: true,
16143                             alwaysShowTabs: false,
16144                             hideTabs: true,
16145                             minTabWidth: 140,
16146                             overflow: 'visible'
16147                          }
16148                      })
16149                 };
16150             } else {
16151             
16152                 // it's a top level one..
16153                 this.parent =  {
16154                     el : new Roo.BorderLayout(el || document.body, {
16155                         center: {
16156                             titlebar: false,
16157                             autoScroll:false,
16158                             closeOnTab: true,
16159                             tabPosition: 'top',
16160                              //resizeTabs: true,
16161                             alwaysShowTabs: el && hp? false :  true,
16162                             hideTabs: el || !hp ? true :  false,
16163                             minTabWidth: 140
16164                          }
16165                     })
16166                 };
16167             }
16168         }
16169         
16170         if (!this.parent.el) {
16171                 // probably an old style ctor, which has been disabled.
16172                 return;
16173
16174         }
16175                 // The 'tree' method is  '_tree now' 
16176             
16177         tree.region = tree.region || this.region;
16178         var is_body = false;
16179         if (this.parent.el === true) {
16180             // bootstrap... - body..
16181             if (el) {
16182                 tree.el = el;
16183             }
16184             this.parent.el = Roo.factory(tree);
16185             is_body = true;
16186         }
16187         
16188         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16189         this.fireEvent('built', this);
16190         
16191         this.panel = this.el;
16192         this.layout = this.panel.layout;
16193         this.parentLayout = this.parent.layout  || false;  
16194          
16195     }
16196     
16197 });
16198
16199 Roo.apply(Roo.XComponent, {
16200     /**
16201      * @property  hideProgress
16202      * true to disable the building progress bar.. usefull on single page renders.
16203      * @type Boolean
16204      */
16205     hideProgress : false,
16206     /**
16207      * @property  buildCompleted
16208      * True when the builder has completed building the interface.
16209      * @type Boolean
16210      */
16211     buildCompleted : false,
16212      
16213     /**
16214      * @property  topModule
16215      * the upper most module - uses document.element as it's constructor.
16216      * @type Object
16217      */
16218      
16219     topModule  : false,
16220       
16221     /**
16222      * @property  modules
16223      * array of modules to be created by registration system.
16224      * @type {Array} of Roo.XComponent
16225      */
16226     
16227     modules : [],
16228     /**
16229      * @property  elmodules
16230      * array of modules to be created by which use #ID 
16231      * @type {Array} of Roo.XComponent
16232      */
16233      
16234     elmodules : [],
16235
16236      /**
16237      * @property  is_alt
16238      * Is an alternative Root - normally used by bootstrap or other systems,
16239      *    where the top element in the tree can wrap 'body' 
16240      * @type {boolean}  (default false)
16241      */
16242      
16243     is_alt : false,
16244     /**
16245      * @property  build_from_html
16246      * Build elements from html - used by bootstrap HTML stuff 
16247      *    - this is cleared after build is completed
16248      * @type {boolean}    (default false)
16249      */
16250      
16251     build_from_html : false,
16252     /**
16253      * Register components to be built later.
16254      *
16255      * This solves the following issues
16256      * - Building is not done on page load, but after an authentication process has occured.
16257      * - Interface elements are registered on page load
16258      * - Parent Interface elements may not be loaded before child, so this handles that..
16259      * 
16260      *
16261      * example:
16262      * 
16263      * MyApp.register({
16264           order : '000001',
16265           module : 'Pman.Tab.projectMgr',
16266           region : 'center',
16267           parent : 'Pman.layout',
16268           disabled : false,  // or use a function..
16269         })
16270      
16271      * * @param {Object} details about module
16272      */
16273     register : function(obj) {
16274                 
16275         Roo.XComponent.event.fireEvent('register', obj);
16276         switch(typeof(obj.disabled) ) {
16277                 
16278             case 'undefined':
16279                 break;
16280             
16281             case 'function':
16282                 if ( obj.disabled() ) {
16283                         return;
16284                 }
16285                 break;
16286             
16287             default:
16288                 if (obj.disabled) {
16289                         return;
16290                 }
16291                 break;
16292         }
16293                 
16294         this.modules.push(obj);
16295          
16296     },
16297     /**
16298      * convert a string to an object..
16299      * eg. 'AAA.BBB' -> finds AAA.BBB
16300
16301      */
16302     
16303     toObject : function(str)
16304     {
16305         if (!str || typeof(str) == 'object') {
16306             return str;
16307         }
16308         if (str.substring(0,1) == '#') {
16309             return str;
16310         }
16311
16312         var ar = str.split('.');
16313         var rt, o;
16314         rt = ar.shift();
16315             /** eval:var:o */
16316         try {
16317             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16318         } catch (e) {
16319             throw "Module not found : " + str;
16320         }
16321         
16322         if (o === false) {
16323             throw "Module not found : " + str;
16324         }
16325         Roo.each(ar, function(e) {
16326             if (typeof(o[e]) == 'undefined') {
16327                 throw "Module not found : " + str;
16328             }
16329             o = o[e];
16330         });
16331         
16332         return o;
16333         
16334     },
16335     
16336     
16337     /**
16338      * move modules into their correct place in the tree..
16339      * 
16340      */
16341     preBuild : function ()
16342     {
16343         var _t = this;
16344         Roo.each(this.modules , function (obj)
16345         {
16346             Roo.XComponent.event.fireEvent('beforebuild', obj);
16347             
16348             var opar = obj.parent;
16349             try { 
16350                 obj.parent = this.toObject(opar);
16351             } catch(e) {
16352                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16353                 return;
16354             }
16355             
16356             if (!obj.parent) {
16357                 Roo.debug && Roo.log("GOT top level module");
16358                 Roo.debug && Roo.log(obj);
16359                 obj.modules = new Roo.util.MixedCollection(false, 
16360                     function(o) { return o.order + '' }
16361                 );
16362                 this.topModule = obj;
16363                 return;
16364             }
16365                         // parent is a string (usually a dom element name..)
16366             if (typeof(obj.parent) == 'string') {
16367                 this.elmodules.push(obj);
16368                 return;
16369             }
16370             if (obj.parent.constructor != Roo.XComponent) {
16371                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16372             }
16373             if (!obj.parent.modules) {
16374                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16375                     function(o) { return o.order + '' }
16376                 );
16377             }
16378             if (obj.parent.disabled) {
16379                 obj.disabled = true;
16380             }
16381             obj.parent.modules.add(obj);
16382         }, this);
16383     },
16384     
16385      /**
16386      * make a list of modules to build.
16387      * @return {Array} list of modules. 
16388      */ 
16389     
16390     buildOrder : function()
16391     {
16392         var _this = this;
16393         var cmp = function(a,b) {   
16394             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16395         };
16396         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16397             throw "No top level modules to build";
16398         }
16399         
16400         // make a flat list in order of modules to build.
16401         var mods = this.topModule ? [ this.topModule ] : [];
16402                 
16403         
16404         // elmodules (is a list of DOM based modules )
16405         Roo.each(this.elmodules, function(e) {
16406             mods.push(e);
16407             if (!this.topModule &&
16408                 typeof(e.parent) == 'string' &&
16409                 e.parent.substring(0,1) == '#' &&
16410                 Roo.get(e.parent.substr(1))
16411                ) {
16412                 
16413                 _this.topModule = e;
16414             }
16415             
16416         });
16417
16418         
16419         // add modules to their parents..
16420         var addMod = function(m) {
16421             Roo.debug && Roo.log("build Order: add: " + m.name);
16422                 
16423             mods.push(m);
16424             if (m.modules && !m.disabled) {
16425                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16426                 m.modules.keySort('ASC',  cmp );
16427                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16428     
16429                 m.modules.each(addMod);
16430             } else {
16431                 Roo.debug && Roo.log("build Order: no child modules");
16432             }
16433             // not sure if this is used any more..
16434             if (m.finalize) {
16435                 m.finalize.name = m.name + " (clean up) ";
16436                 mods.push(m.finalize);
16437             }
16438             
16439         }
16440         if (this.topModule && this.topModule.modules) { 
16441             this.topModule.modules.keySort('ASC',  cmp );
16442             this.topModule.modules.each(addMod);
16443         } 
16444         return mods;
16445     },
16446     
16447      /**
16448      * Build the registered modules.
16449      * @param {Object} parent element.
16450      * @param {Function} optional method to call after module has been added.
16451      * 
16452      */ 
16453    
16454     build : function(opts) 
16455     {
16456         
16457         if (typeof(opts) != 'undefined') {
16458             Roo.apply(this,opts);
16459         }
16460         
16461         this.preBuild();
16462         var mods = this.buildOrder();
16463       
16464         //this.allmods = mods;
16465         //Roo.debug && Roo.log(mods);
16466         //return;
16467         if (!mods.length) { // should not happen
16468             throw "NO modules!!!";
16469         }
16470         
16471         
16472         var msg = "Building Interface...";
16473         // flash it up as modal - so we store the mask!?
16474         if (!this.hideProgress && Roo.MessageBox) {
16475             Roo.MessageBox.show({ title: 'loading' });
16476             Roo.MessageBox.show({
16477                title: "Please wait...",
16478                msg: msg,
16479                width:450,
16480                progress:true,
16481                closable:false,
16482                modal: false
16483               
16484             });
16485         }
16486         var total = mods.length;
16487         
16488         var _this = this;
16489         var progressRun = function() {
16490             if (!mods.length) {
16491                 Roo.debug && Roo.log('hide?');
16492                 if (!this.hideProgress && Roo.MessageBox) {
16493                     Roo.MessageBox.hide();
16494                 }
16495                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16496                 
16497                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16498                 
16499                 // THE END...
16500                 return false;   
16501             }
16502             
16503             var m = mods.shift();
16504             
16505             
16506             Roo.debug && Roo.log(m);
16507             // not sure if this is supported any more.. - modules that are are just function
16508             if (typeof(m) == 'function') { 
16509                 m.call(this);
16510                 return progressRun.defer(10, _this);
16511             } 
16512             
16513             
16514             msg = "Building Interface " + (total  - mods.length) + 
16515                     " of " + total + 
16516                     (m.name ? (' - ' + m.name) : '');
16517                         Roo.debug && Roo.log(msg);
16518             if (!this.hideProgress &&  Roo.MessageBox) { 
16519                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16520             }
16521             
16522          
16523             // is the module disabled?
16524             var disabled = (typeof(m.disabled) == 'function') ?
16525                 m.disabled.call(m.module.disabled) : m.disabled;    
16526             
16527             
16528             if (disabled) {
16529                 return progressRun(); // we do not update the display!
16530             }
16531             
16532             // now build 
16533             
16534                         
16535                         
16536             m.render();
16537             // it's 10 on top level, and 1 on others??? why...
16538             return progressRun.defer(10, _this);
16539              
16540         }
16541         progressRun.defer(1, _this);
16542      
16543         
16544         
16545     },
16546         
16547         
16548         /**
16549          * Event Object.
16550          *
16551          *
16552          */
16553         event: false, 
16554     /**
16555          * wrapper for event.on - aliased later..  
16556          * Typically use to register a event handler for register:
16557          *
16558          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16559          *
16560          */
16561     on : false
16562    
16563     
16564     
16565 });
16566
16567 Roo.XComponent.event = new Roo.util.Observable({
16568                 events : { 
16569                         /**
16570                          * @event register
16571                          * Fires when an Component is registered,
16572                          * set the disable property on the Component to stop registration.
16573                          * @param {Roo.XComponent} c the component being registerd.
16574                          * 
16575                          */
16576                         'register' : true,
16577             /**
16578                          * @event beforebuild
16579                          * Fires before each Component is built
16580                          * can be used to apply permissions.
16581                          * @param {Roo.XComponent} c the component being registerd.
16582                          * 
16583                          */
16584                         'beforebuild' : true,
16585                         /**
16586                          * @event buildcomplete
16587                          * Fires on the top level element when all elements have been built
16588                          * @param {Roo.XComponent} the top level component.
16589                          */
16590                         'buildcomplete' : true
16591                         
16592                 }
16593 });
16594
16595 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16596  //
16597  /**
16598  * marked - a markdown parser
16599  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16600  * https://github.com/chjj/marked
16601  */
16602
16603
16604 /**
16605  *
16606  * Roo.Markdown - is a very crude wrapper around marked..
16607  *
16608  * usage:
16609  * 
16610  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16611  * 
16612  * Note: move the sample code to the bottom of this
16613  * file before uncommenting it.
16614  *
16615  */
16616
16617 Roo.Markdown = {};
16618 Roo.Markdown.toHtml = function(text) {
16619     
16620     var c = new Roo.Markdown.marked.setOptions({
16621             renderer: new Roo.Markdown.marked.Renderer(),
16622             gfm: true,
16623             tables: true,
16624             breaks: false,
16625             pedantic: false,
16626             sanitize: false,
16627             smartLists: true,
16628             smartypants: false
16629           });
16630     // A FEW HACKS!!?
16631     
16632     text = text.replace(/\\\n/g,' ');
16633     return Roo.Markdown.marked(text);
16634 };
16635 //
16636 // converter
16637 //
16638 // Wraps all "globals" so that the only thing
16639 // exposed is makeHtml().
16640 //
16641 (function() {
16642     
16643     /**
16644      * Block-Level Grammar
16645      */
16646     
16647     var block = {
16648       newline: /^\n+/,
16649       code: /^( {4}[^\n]+\n*)+/,
16650       fences: noop,
16651       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16652       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16653       nptable: noop,
16654       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16655       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16656       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16657       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16658       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16659       table: noop,
16660       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16661       text: /^[^\n]+/
16662     };
16663     
16664     block.bullet = /(?:[*+-]|\d+\.)/;
16665     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16666     block.item = replace(block.item, 'gm')
16667       (/bull/g, block.bullet)
16668       ();
16669     
16670     block.list = replace(block.list)
16671       (/bull/g, block.bullet)
16672       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16673       ('def', '\\n+(?=' + block.def.source + ')')
16674       ();
16675     
16676     block.blockquote = replace(block.blockquote)
16677       ('def', block.def)
16678       ();
16679     
16680     block._tag = '(?!(?:'
16681       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16682       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16683       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16684     
16685     block.html = replace(block.html)
16686       ('comment', /<!--[\s\S]*?-->/)
16687       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16688       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16689       (/tag/g, block._tag)
16690       ();
16691     
16692     block.paragraph = replace(block.paragraph)
16693       ('hr', block.hr)
16694       ('heading', block.heading)
16695       ('lheading', block.lheading)
16696       ('blockquote', block.blockquote)
16697       ('tag', '<' + block._tag)
16698       ('def', block.def)
16699       ();
16700     
16701     /**
16702      * Normal Block Grammar
16703      */
16704     
16705     block.normal = merge({}, block);
16706     
16707     /**
16708      * GFM Block Grammar
16709      */
16710     
16711     block.gfm = merge({}, block.normal, {
16712       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16713       paragraph: /^/,
16714       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16715     });
16716     
16717     block.gfm.paragraph = replace(block.paragraph)
16718       ('(?!', '(?!'
16719         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16720         + block.list.source.replace('\\1', '\\3') + '|')
16721       ();
16722     
16723     /**
16724      * GFM + Tables Block Grammar
16725      */
16726     
16727     block.tables = merge({}, block.gfm, {
16728       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16729       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16730     });
16731     
16732     /**
16733      * Block Lexer
16734      */
16735     
16736     function Lexer(options) {
16737       this.tokens = [];
16738       this.tokens.links = {};
16739       this.options = options || marked.defaults;
16740       this.rules = block.normal;
16741     
16742       if (this.options.gfm) {
16743         if (this.options.tables) {
16744           this.rules = block.tables;
16745         } else {
16746           this.rules = block.gfm;
16747         }
16748       }
16749     }
16750     
16751     /**
16752      * Expose Block Rules
16753      */
16754     
16755     Lexer.rules = block;
16756     
16757     /**
16758      * Static Lex Method
16759      */
16760     
16761     Lexer.lex = function(src, options) {
16762       var lexer = new Lexer(options);
16763       return lexer.lex(src);
16764     };
16765     
16766     /**
16767      * Preprocessing
16768      */
16769     
16770     Lexer.prototype.lex = function(src) {
16771       src = src
16772         .replace(/\r\n|\r/g, '\n')
16773         .replace(/\t/g, '    ')
16774         .replace(/\u00a0/g, ' ')
16775         .replace(/\u2424/g, '\n');
16776     
16777       return this.token(src, true);
16778     };
16779     
16780     /**
16781      * Lexing
16782      */
16783     
16784     Lexer.prototype.token = function(src, top, bq) {
16785       var src = src.replace(/^ +$/gm, '')
16786         , next
16787         , loose
16788         , cap
16789         , bull
16790         , b
16791         , item
16792         , space
16793         , i
16794         , l;
16795     
16796       while (src) {
16797         // newline
16798         if (cap = this.rules.newline.exec(src)) {
16799           src = src.substring(cap[0].length);
16800           if (cap[0].length > 1) {
16801             this.tokens.push({
16802               type: 'space'
16803             });
16804           }
16805         }
16806     
16807         // code
16808         if (cap = this.rules.code.exec(src)) {
16809           src = src.substring(cap[0].length);
16810           cap = cap[0].replace(/^ {4}/gm, '');
16811           this.tokens.push({
16812             type: 'code',
16813             text: !this.options.pedantic
16814               ? cap.replace(/\n+$/, '')
16815               : cap
16816           });
16817           continue;
16818         }
16819     
16820         // fences (gfm)
16821         if (cap = this.rules.fences.exec(src)) {
16822           src = src.substring(cap[0].length);
16823           this.tokens.push({
16824             type: 'code',
16825             lang: cap[2],
16826             text: cap[3] || ''
16827           });
16828           continue;
16829         }
16830     
16831         // heading
16832         if (cap = this.rules.heading.exec(src)) {
16833           src = src.substring(cap[0].length);
16834           this.tokens.push({
16835             type: 'heading',
16836             depth: cap[1].length,
16837             text: cap[2]
16838           });
16839           continue;
16840         }
16841     
16842         // table no leading pipe (gfm)
16843         if (top && (cap = this.rules.nptable.exec(src))) {
16844           src = src.substring(cap[0].length);
16845     
16846           item = {
16847             type: 'table',
16848             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16849             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16850             cells: cap[3].replace(/\n$/, '').split('\n')
16851           };
16852     
16853           for (i = 0; i < item.align.length; i++) {
16854             if (/^ *-+: *$/.test(item.align[i])) {
16855               item.align[i] = 'right';
16856             } else if (/^ *:-+: *$/.test(item.align[i])) {
16857               item.align[i] = 'center';
16858             } else if (/^ *:-+ *$/.test(item.align[i])) {
16859               item.align[i] = 'left';
16860             } else {
16861               item.align[i] = null;
16862             }
16863           }
16864     
16865           for (i = 0; i < item.cells.length; i++) {
16866             item.cells[i] = item.cells[i].split(/ *\| */);
16867           }
16868     
16869           this.tokens.push(item);
16870     
16871           continue;
16872         }
16873     
16874         // lheading
16875         if (cap = this.rules.lheading.exec(src)) {
16876           src = src.substring(cap[0].length);
16877           this.tokens.push({
16878             type: 'heading',
16879             depth: cap[2] === '=' ? 1 : 2,
16880             text: cap[1]
16881           });
16882           continue;
16883         }
16884     
16885         // hr
16886         if (cap = this.rules.hr.exec(src)) {
16887           src = src.substring(cap[0].length);
16888           this.tokens.push({
16889             type: 'hr'
16890           });
16891           continue;
16892         }
16893     
16894         // blockquote
16895         if (cap = this.rules.blockquote.exec(src)) {
16896           src = src.substring(cap[0].length);
16897     
16898           this.tokens.push({
16899             type: 'blockquote_start'
16900           });
16901     
16902           cap = cap[0].replace(/^ *> ?/gm, '');
16903     
16904           // Pass `top` to keep the current
16905           // "toplevel" state. This is exactly
16906           // how markdown.pl works.
16907           this.token(cap, top, true);
16908     
16909           this.tokens.push({
16910             type: 'blockquote_end'
16911           });
16912     
16913           continue;
16914         }
16915     
16916         // list
16917         if (cap = this.rules.list.exec(src)) {
16918           src = src.substring(cap[0].length);
16919           bull = cap[2];
16920     
16921           this.tokens.push({
16922             type: 'list_start',
16923             ordered: bull.length > 1
16924           });
16925     
16926           // Get each top-level item.
16927           cap = cap[0].match(this.rules.item);
16928     
16929           next = false;
16930           l = cap.length;
16931           i = 0;
16932     
16933           for (; i < l; i++) {
16934             item = cap[i];
16935     
16936             // Remove the list item's bullet
16937             // so it is seen as the next token.
16938             space = item.length;
16939             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16940     
16941             // Outdent whatever the
16942             // list item contains. Hacky.
16943             if (~item.indexOf('\n ')) {
16944               space -= item.length;
16945               item = !this.options.pedantic
16946                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16947                 : item.replace(/^ {1,4}/gm, '');
16948             }
16949     
16950             // Determine whether the next list item belongs here.
16951             // Backpedal if it does not belong in this list.
16952             if (this.options.smartLists && i !== l - 1) {
16953               b = block.bullet.exec(cap[i + 1])[0];
16954               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16955                 src = cap.slice(i + 1).join('\n') + src;
16956                 i = l - 1;
16957               }
16958             }
16959     
16960             // Determine whether item is loose or not.
16961             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16962             // for discount behavior.
16963             loose = next || /\n\n(?!\s*$)/.test(item);
16964             if (i !== l - 1) {
16965               next = item.charAt(item.length - 1) === '\n';
16966               if (!loose) { loose = next; }
16967             }
16968     
16969             this.tokens.push({
16970               type: loose
16971                 ? 'loose_item_start'
16972                 : 'list_item_start'
16973             });
16974     
16975             // Recurse.
16976             this.token(item, false, bq);
16977     
16978             this.tokens.push({
16979               type: 'list_item_end'
16980             });
16981           }
16982     
16983           this.tokens.push({
16984             type: 'list_end'
16985           });
16986     
16987           continue;
16988         }
16989     
16990         // html
16991         if (cap = this.rules.html.exec(src)) {
16992           src = src.substring(cap[0].length);
16993           this.tokens.push({
16994             type: this.options.sanitize
16995               ? 'paragraph'
16996               : 'html',
16997             pre: !this.options.sanitizer
16998               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
16999             text: cap[0]
17000           });
17001           continue;
17002         }
17003     
17004         // def
17005         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17006           src = src.substring(cap[0].length);
17007           this.tokens.links[cap[1].toLowerCase()] = {
17008             href: cap[2],
17009             title: cap[3]
17010           };
17011           continue;
17012         }
17013     
17014         // table (gfm)
17015         if (top && (cap = this.rules.table.exec(src))) {
17016           src = src.substring(cap[0].length);
17017     
17018           item = {
17019             type: 'table',
17020             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17021             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17022             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17023           };
17024     
17025           for (i = 0; i < item.align.length; i++) {
17026             if (/^ *-+: *$/.test(item.align[i])) {
17027               item.align[i] = 'right';
17028             } else if (/^ *:-+: *$/.test(item.align[i])) {
17029               item.align[i] = 'center';
17030             } else if (/^ *:-+ *$/.test(item.align[i])) {
17031               item.align[i] = 'left';
17032             } else {
17033               item.align[i] = null;
17034             }
17035           }
17036     
17037           for (i = 0; i < item.cells.length; i++) {
17038             item.cells[i] = item.cells[i]
17039               .replace(/^ *\| *| *\| *$/g, '')
17040               .split(/ *\| */);
17041           }
17042     
17043           this.tokens.push(item);
17044     
17045           continue;
17046         }
17047     
17048         // top-level paragraph
17049         if (top && (cap = this.rules.paragraph.exec(src))) {
17050           src = src.substring(cap[0].length);
17051           this.tokens.push({
17052             type: 'paragraph',
17053             text: cap[1].charAt(cap[1].length - 1) === '\n'
17054               ? cap[1].slice(0, -1)
17055               : cap[1]
17056           });
17057           continue;
17058         }
17059     
17060         // text
17061         if (cap = this.rules.text.exec(src)) {
17062           // Top-level should never reach here.
17063           src = src.substring(cap[0].length);
17064           this.tokens.push({
17065             type: 'text',
17066             text: cap[0]
17067           });
17068           continue;
17069         }
17070     
17071         if (src) {
17072           throw new
17073             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17074         }
17075       }
17076     
17077       return this.tokens;
17078     };
17079     
17080     /**
17081      * Inline-Level Grammar
17082      */
17083     
17084     var inline = {
17085       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17086       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17087       url: noop,
17088       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17089       link: /^!?\[(inside)\]\(href\)/,
17090       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17091       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17092       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17093       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17094       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17095       br: /^ {2,}\n(?!\s*$)/,
17096       del: noop,
17097       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17098     };
17099     
17100     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17101     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17102     
17103     inline.link = replace(inline.link)
17104       ('inside', inline._inside)
17105       ('href', inline._href)
17106       ();
17107     
17108     inline.reflink = replace(inline.reflink)
17109       ('inside', inline._inside)
17110       ();
17111     
17112     /**
17113      * Normal Inline Grammar
17114      */
17115     
17116     inline.normal = merge({}, inline);
17117     
17118     /**
17119      * Pedantic Inline Grammar
17120      */
17121     
17122     inline.pedantic = merge({}, inline.normal, {
17123       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17124       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17125     });
17126     
17127     /**
17128      * GFM Inline Grammar
17129      */
17130     
17131     inline.gfm = merge({}, inline.normal, {
17132       escape: replace(inline.escape)('])', '~|])')(),
17133       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17134       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17135       text: replace(inline.text)
17136         (']|', '~]|')
17137         ('|', '|https?://|')
17138         ()
17139     });
17140     
17141     /**
17142      * GFM + Line Breaks Inline Grammar
17143      */
17144     
17145     inline.breaks = merge({}, inline.gfm, {
17146       br: replace(inline.br)('{2,}', '*')(),
17147       text: replace(inline.gfm.text)('{2,}', '*')()
17148     });
17149     
17150     /**
17151      * Inline Lexer & Compiler
17152      */
17153     
17154     function InlineLexer(links, options) {
17155       this.options = options || marked.defaults;
17156       this.links = links;
17157       this.rules = inline.normal;
17158       this.renderer = this.options.renderer || new Renderer;
17159       this.renderer.options = this.options;
17160     
17161       if (!this.links) {
17162         throw new
17163           Error('Tokens array requires a `links` property.');
17164       }
17165     
17166       if (this.options.gfm) {
17167         if (this.options.breaks) {
17168           this.rules = inline.breaks;
17169         } else {
17170           this.rules = inline.gfm;
17171         }
17172       } else if (this.options.pedantic) {
17173         this.rules = inline.pedantic;
17174       }
17175     }
17176     
17177     /**
17178      * Expose Inline Rules
17179      */
17180     
17181     InlineLexer.rules = inline;
17182     
17183     /**
17184      * Static Lexing/Compiling Method
17185      */
17186     
17187     InlineLexer.output = function(src, links, options) {
17188       var inline = new InlineLexer(links, options);
17189       return inline.output(src);
17190     };
17191     
17192     /**
17193      * Lexing/Compiling
17194      */
17195     
17196     InlineLexer.prototype.output = function(src) {
17197       var out = ''
17198         , link
17199         , text
17200         , href
17201         , cap;
17202     
17203       while (src) {
17204         // escape
17205         if (cap = this.rules.escape.exec(src)) {
17206           src = src.substring(cap[0].length);
17207           out += cap[1];
17208           continue;
17209         }
17210     
17211         // autolink
17212         if (cap = this.rules.autolink.exec(src)) {
17213           src = src.substring(cap[0].length);
17214           if (cap[2] === '@') {
17215             text = cap[1].charAt(6) === ':'
17216               ? this.mangle(cap[1].substring(7))
17217               : this.mangle(cap[1]);
17218             href = this.mangle('mailto:') + text;
17219           } else {
17220             text = escape(cap[1]);
17221             href = text;
17222           }
17223           out += this.renderer.link(href, null, text);
17224           continue;
17225         }
17226     
17227         // url (gfm)
17228         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17229           src = src.substring(cap[0].length);
17230           text = escape(cap[1]);
17231           href = text;
17232           out += this.renderer.link(href, null, text);
17233           continue;
17234         }
17235     
17236         // tag
17237         if (cap = this.rules.tag.exec(src)) {
17238           if (!this.inLink && /^<a /i.test(cap[0])) {
17239             this.inLink = true;
17240           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17241             this.inLink = false;
17242           }
17243           src = src.substring(cap[0].length);
17244           out += this.options.sanitize
17245             ? this.options.sanitizer
17246               ? this.options.sanitizer(cap[0])
17247               : escape(cap[0])
17248             : cap[0];
17249           continue;
17250         }
17251     
17252         // link
17253         if (cap = this.rules.link.exec(src)) {
17254           src = src.substring(cap[0].length);
17255           this.inLink = true;
17256           out += this.outputLink(cap, {
17257             href: cap[2],
17258             title: cap[3]
17259           });
17260           this.inLink = false;
17261           continue;
17262         }
17263     
17264         // reflink, nolink
17265         if ((cap = this.rules.reflink.exec(src))
17266             || (cap = this.rules.nolink.exec(src))) {
17267           src = src.substring(cap[0].length);
17268           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17269           link = this.links[link.toLowerCase()];
17270           if (!link || !link.href) {
17271             out += cap[0].charAt(0);
17272             src = cap[0].substring(1) + src;
17273             continue;
17274           }
17275           this.inLink = true;
17276           out += this.outputLink(cap, link);
17277           this.inLink = false;
17278           continue;
17279         }
17280     
17281         // strong
17282         if (cap = this.rules.strong.exec(src)) {
17283           src = src.substring(cap[0].length);
17284           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17285           continue;
17286         }
17287     
17288         // em
17289         if (cap = this.rules.em.exec(src)) {
17290           src = src.substring(cap[0].length);
17291           out += this.renderer.em(this.output(cap[2] || cap[1]));
17292           continue;
17293         }
17294     
17295         // code
17296         if (cap = this.rules.code.exec(src)) {
17297           src = src.substring(cap[0].length);
17298           out += this.renderer.codespan(escape(cap[2], true));
17299           continue;
17300         }
17301     
17302         // br
17303         if (cap = this.rules.br.exec(src)) {
17304           src = src.substring(cap[0].length);
17305           out += this.renderer.br();
17306           continue;
17307         }
17308     
17309         // del (gfm)
17310         if (cap = this.rules.del.exec(src)) {
17311           src = src.substring(cap[0].length);
17312           out += this.renderer.del(this.output(cap[1]));
17313           continue;
17314         }
17315     
17316         // text
17317         if (cap = this.rules.text.exec(src)) {
17318           src = src.substring(cap[0].length);
17319           out += this.renderer.text(escape(this.smartypants(cap[0])));
17320           continue;
17321         }
17322     
17323         if (src) {
17324           throw new
17325             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17326         }
17327       }
17328     
17329       return out;
17330     };
17331     
17332     /**
17333      * Compile Link
17334      */
17335     
17336     InlineLexer.prototype.outputLink = function(cap, link) {
17337       var href = escape(link.href)
17338         , title = link.title ? escape(link.title) : null;
17339     
17340       return cap[0].charAt(0) !== '!'
17341         ? this.renderer.link(href, title, this.output(cap[1]))
17342         : this.renderer.image(href, title, escape(cap[1]));
17343     };
17344     
17345     /**
17346      * Smartypants Transformations
17347      */
17348     
17349     InlineLexer.prototype.smartypants = function(text) {
17350       if (!this.options.smartypants)  { return text; }
17351       return text
17352         // em-dashes
17353         .replace(/---/g, '\u2014')
17354         // en-dashes
17355         .replace(/--/g, '\u2013')
17356         // opening singles
17357         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17358         // closing singles & apostrophes
17359         .replace(/'/g, '\u2019')
17360         // opening doubles
17361         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17362         // closing doubles
17363         .replace(/"/g, '\u201d')
17364         // ellipses
17365         .replace(/\.{3}/g, '\u2026');
17366     };
17367     
17368     /**
17369      * Mangle Links
17370      */
17371     
17372     InlineLexer.prototype.mangle = function(text) {
17373       if (!this.options.mangle) { return text; }
17374       var out = ''
17375         , l = text.length
17376         , i = 0
17377         , ch;
17378     
17379       for (; i < l; i++) {
17380         ch = text.charCodeAt(i);
17381         if (Math.random() > 0.5) {
17382           ch = 'x' + ch.toString(16);
17383         }
17384         out += '&#' + ch + ';';
17385       }
17386     
17387       return out;
17388     };
17389     
17390     /**
17391      * Renderer
17392      */
17393     
17394     function Renderer(options) {
17395       this.options = options || {};
17396     }
17397     
17398     Renderer.prototype.code = function(code, lang, escaped) {
17399       if (this.options.highlight) {
17400         var out = this.options.highlight(code, lang);
17401         if (out != null && out !== code) {
17402           escaped = true;
17403           code = out;
17404         }
17405       } else {
17406             // hack!!! - it's already escapeD?
17407             escaped = true;
17408       }
17409     
17410       if (!lang) {
17411         return '<pre><code>'
17412           + (escaped ? code : escape(code, true))
17413           + '\n</code></pre>';
17414       }
17415     
17416       return '<pre><code class="'
17417         + this.options.langPrefix
17418         + escape(lang, true)
17419         + '">'
17420         + (escaped ? code : escape(code, true))
17421         + '\n</code></pre>\n';
17422     };
17423     
17424     Renderer.prototype.blockquote = function(quote) {
17425       return '<blockquote>\n' + quote + '</blockquote>\n';
17426     };
17427     
17428     Renderer.prototype.html = function(html) {
17429       return html;
17430     };
17431     
17432     Renderer.prototype.heading = function(text, level, raw) {
17433       return '<h'
17434         + level
17435         + ' id="'
17436         + this.options.headerPrefix
17437         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17438         + '">'
17439         + text
17440         + '</h'
17441         + level
17442         + '>\n';
17443     };
17444     
17445     Renderer.prototype.hr = function() {
17446       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17447     };
17448     
17449     Renderer.prototype.list = function(body, ordered) {
17450       var type = ordered ? 'ol' : 'ul';
17451       return '<' + type + '>\n' + body + '</' + type + '>\n';
17452     };
17453     
17454     Renderer.prototype.listitem = function(text) {
17455       return '<li>' + text + '</li>\n';
17456     };
17457     
17458     Renderer.prototype.paragraph = function(text) {
17459       return '<p>' + text + '</p>\n';
17460     };
17461     
17462     Renderer.prototype.table = function(header, body) {
17463       return '<table class="table table-striped">\n'
17464         + '<thead>\n'
17465         + header
17466         + '</thead>\n'
17467         + '<tbody>\n'
17468         + body
17469         + '</tbody>\n'
17470         + '</table>\n';
17471     };
17472     
17473     Renderer.prototype.tablerow = function(content) {
17474       return '<tr>\n' + content + '</tr>\n';
17475     };
17476     
17477     Renderer.prototype.tablecell = function(content, flags) {
17478       var type = flags.header ? 'th' : 'td';
17479       var tag = flags.align
17480         ? '<' + type + ' style="text-align:' + flags.align + '">'
17481         : '<' + type + '>';
17482       return tag + content + '</' + type + '>\n';
17483     };
17484     
17485     // span level renderer
17486     Renderer.prototype.strong = function(text) {
17487       return '<strong>' + text + '</strong>';
17488     };
17489     
17490     Renderer.prototype.em = function(text) {
17491       return '<em>' + text + '</em>';
17492     };
17493     
17494     Renderer.prototype.codespan = function(text) {
17495       return '<code>' + text + '</code>';
17496     };
17497     
17498     Renderer.prototype.br = function() {
17499       return this.options.xhtml ? '<br/>' : '<br>';
17500     };
17501     
17502     Renderer.prototype.del = function(text) {
17503       return '<del>' + text + '</del>';
17504     };
17505     
17506     Renderer.prototype.link = function(href, title, text) {
17507       if (this.options.sanitize) {
17508         try {
17509           var prot = decodeURIComponent(unescape(href))
17510             .replace(/[^\w:]/g, '')
17511             .toLowerCase();
17512         } catch (e) {
17513           return '';
17514         }
17515         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17516           return '';
17517         }
17518       }
17519       var out = '<a href="' + href + '"';
17520       if (title) {
17521         out += ' title="' + title + '"';
17522       }
17523       out += '>' + text + '</a>';
17524       return out;
17525     };
17526     
17527     Renderer.prototype.image = function(href, title, text) {
17528       var out = '<img src="' + href + '" alt="' + text + '"';
17529       if (title) {
17530         out += ' title="' + title + '"';
17531       }
17532       out += this.options.xhtml ? '/>' : '>';
17533       return out;
17534     };
17535     
17536     Renderer.prototype.text = function(text) {
17537       return text;
17538     };
17539     
17540     /**
17541      * Parsing & Compiling
17542      */
17543     
17544     function Parser(options) {
17545       this.tokens = [];
17546       this.token = null;
17547       this.options = options || marked.defaults;
17548       this.options.renderer = this.options.renderer || new Renderer;
17549       this.renderer = this.options.renderer;
17550       this.renderer.options = this.options;
17551     }
17552     
17553     /**
17554      * Static Parse Method
17555      */
17556     
17557     Parser.parse = function(src, options, renderer) {
17558       var parser = new Parser(options, renderer);
17559       return parser.parse(src);
17560     };
17561     
17562     /**
17563      * Parse Loop
17564      */
17565     
17566     Parser.prototype.parse = function(src) {
17567       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17568       this.tokens = src.reverse();
17569     
17570       var out = '';
17571       while (this.next()) {
17572         out += this.tok();
17573       }
17574     
17575       return out;
17576     };
17577     
17578     /**
17579      * Next Token
17580      */
17581     
17582     Parser.prototype.next = function() {
17583       return this.token = this.tokens.pop();
17584     };
17585     
17586     /**
17587      * Preview Next Token
17588      */
17589     
17590     Parser.prototype.peek = function() {
17591       return this.tokens[this.tokens.length - 1] || 0;
17592     };
17593     
17594     /**
17595      * Parse Text Tokens
17596      */
17597     
17598     Parser.prototype.parseText = function() {
17599       var body = this.token.text;
17600     
17601       while (this.peek().type === 'text') {
17602         body += '\n' + this.next().text;
17603       }
17604     
17605       return this.inline.output(body);
17606     };
17607     
17608     /**
17609      * Parse Current Token
17610      */
17611     
17612     Parser.prototype.tok = function() {
17613       switch (this.token.type) {
17614         case 'space': {
17615           return '';
17616         }
17617         case 'hr': {
17618           return this.renderer.hr();
17619         }
17620         case 'heading': {
17621           return this.renderer.heading(
17622             this.inline.output(this.token.text),
17623             this.token.depth,
17624             this.token.text);
17625         }
17626         case 'code': {
17627           return this.renderer.code(this.token.text,
17628             this.token.lang,
17629             this.token.escaped);
17630         }
17631         case 'table': {
17632           var header = ''
17633             , body = ''
17634             , i
17635             , row
17636             , cell
17637             , flags
17638             , j;
17639     
17640           // header
17641           cell = '';
17642           for (i = 0; i < this.token.header.length; i++) {
17643             flags = { header: true, align: this.token.align[i] };
17644             cell += this.renderer.tablecell(
17645               this.inline.output(this.token.header[i]),
17646               { header: true, align: this.token.align[i] }
17647             );
17648           }
17649           header += this.renderer.tablerow(cell);
17650     
17651           for (i = 0; i < this.token.cells.length; i++) {
17652             row = this.token.cells[i];
17653     
17654             cell = '';
17655             for (j = 0; j < row.length; j++) {
17656               cell += this.renderer.tablecell(
17657                 this.inline.output(row[j]),
17658                 { header: false, align: this.token.align[j] }
17659               );
17660             }
17661     
17662             body += this.renderer.tablerow(cell);
17663           }
17664           return this.renderer.table(header, body);
17665         }
17666         case 'blockquote_start': {
17667           var body = '';
17668     
17669           while (this.next().type !== 'blockquote_end') {
17670             body += this.tok();
17671           }
17672     
17673           return this.renderer.blockquote(body);
17674         }
17675         case 'list_start': {
17676           var body = ''
17677             , ordered = this.token.ordered;
17678     
17679           while (this.next().type !== 'list_end') {
17680             body += this.tok();
17681           }
17682     
17683           return this.renderer.list(body, ordered);
17684         }
17685         case 'list_item_start': {
17686           var body = '';
17687     
17688           while (this.next().type !== 'list_item_end') {
17689             body += this.token.type === 'text'
17690               ? this.parseText()
17691               : this.tok();
17692           }
17693     
17694           return this.renderer.listitem(body);
17695         }
17696         case 'loose_item_start': {
17697           var body = '';
17698     
17699           while (this.next().type !== 'list_item_end') {
17700             body += this.tok();
17701           }
17702     
17703           return this.renderer.listitem(body);
17704         }
17705         case 'html': {
17706           var html = !this.token.pre && !this.options.pedantic
17707             ? this.inline.output(this.token.text)
17708             : this.token.text;
17709           return this.renderer.html(html);
17710         }
17711         case 'paragraph': {
17712           return this.renderer.paragraph(this.inline.output(this.token.text));
17713         }
17714         case 'text': {
17715           return this.renderer.paragraph(this.parseText());
17716         }
17717       }
17718     };
17719     
17720     /**
17721      * Helpers
17722      */
17723     
17724     function escape(html, encode) {
17725       return html
17726         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17727         .replace(/</g, '&lt;')
17728         .replace(/>/g, '&gt;')
17729         .replace(/"/g, '&quot;')
17730         .replace(/'/g, '&#39;');
17731     }
17732     
17733     function unescape(html) {
17734         // explicitly match decimal, hex, and named HTML entities 
17735       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17736         n = n.toLowerCase();
17737         if (n === 'colon') { return ':'; }
17738         if (n.charAt(0) === '#') {
17739           return n.charAt(1) === 'x'
17740             ? String.fromCharCode(parseInt(n.substring(2), 16))
17741             : String.fromCharCode(+n.substring(1));
17742         }
17743         return '';
17744       });
17745     }
17746     
17747     function replace(regex, opt) {
17748       regex = regex.source;
17749       opt = opt || '';
17750       return function self(name, val) {
17751         if (!name) { return new RegExp(regex, opt); }
17752         val = val.source || val;
17753         val = val.replace(/(^|[^\[])\^/g, '$1');
17754         regex = regex.replace(name, val);
17755         return self;
17756       };
17757     }
17758     
17759     function noop() {}
17760     noop.exec = noop;
17761     
17762     function merge(obj) {
17763       var i = 1
17764         , target
17765         , key;
17766     
17767       for (; i < arguments.length; i++) {
17768         target = arguments[i];
17769         for (key in target) {
17770           if (Object.prototype.hasOwnProperty.call(target, key)) {
17771             obj[key] = target[key];
17772           }
17773         }
17774       }
17775     
17776       return obj;
17777     }
17778     
17779     
17780     /**
17781      * Marked
17782      */
17783     
17784     function marked(src, opt, callback) {
17785       if (callback || typeof opt === 'function') {
17786         if (!callback) {
17787           callback = opt;
17788           opt = null;
17789         }
17790     
17791         opt = merge({}, marked.defaults, opt || {});
17792     
17793         var highlight = opt.highlight
17794           , tokens
17795           , pending
17796           , i = 0;
17797     
17798         try {
17799           tokens = Lexer.lex(src, opt)
17800         } catch (e) {
17801           return callback(e);
17802         }
17803     
17804         pending = tokens.length;
17805     
17806         var done = function(err) {
17807           if (err) {
17808             opt.highlight = highlight;
17809             return callback(err);
17810           }
17811     
17812           var out;
17813     
17814           try {
17815             out = Parser.parse(tokens, opt);
17816           } catch (e) {
17817             err = e;
17818           }
17819     
17820           opt.highlight = highlight;
17821     
17822           return err
17823             ? callback(err)
17824             : callback(null, out);
17825         };
17826     
17827         if (!highlight || highlight.length < 3) {
17828           return done();
17829         }
17830     
17831         delete opt.highlight;
17832     
17833         if (!pending) { return done(); }
17834     
17835         for (; i < tokens.length; i++) {
17836           (function(token) {
17837             if (token.type !== 'code') {
17838               return --pending || done();
17839             }
17840             return highlight(token.text, token.lang, function(err, code) {
17841               if (err) { return done(err); }
17842               if (code == null || code === token.text) {
17843                 return --pending || done();
17844               }
17845               token.text = code;
17846               token.escaped = true;
17847               --pending || done();
17848             });
17849           })(tokens[i]);
17850         }
17851     
17852         return;
17853       }
17854       try {
17855         if (opt) { opt = merge({}, marked.defaults, opt); }
17856         return Parser.parse(Lexer.lex(src, opt), opt);
17857       } catch (e) {
17858         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17859         if ((opt || marked.defaults).silent) {
17860           return '<p>An error occured:</p><pre>'
17861             + escape(e.message + '', true)
17862             + '</pre>';
17863         }
17864         throw e;
17865       }
17866     }
17867     
17868     /**
17869      * Options
17870      */
17871     
17872     marked.options =
17873     marked.setOptions = function(opt) {
17874       merge(marked.defaults, opt);
17875       return marked;
17876     };
17877     
17878     marked.defaults = {
17879       gfm: true,
17880       tables: true,
17881       breaks: false,
17882       pedantic: false,
17883       sanitize: false,
17884       sanitizer: null,
17885       mangle: true,
17886       smartLists: false,
17887       silent: false,
17888       highlight: null,
17889       langPrefix: 'lang-',
17890       smartypants: false,
17891       headerPrefix: '',
17892       renderer: new Renderer,
17893       xhtml: false
17894     };
17895     
17896     /**
17897      * Expose
17898      */
17899     
17900     marked.Parser = Parser;
17901     marked.parser = Parser.parse;
17902     
17903     marked.Renderer = Renderer;
17904     
17905     marked.Lexer = Lexer;
17906     marked.lexer = Lexer.lex;
17907     
17908     marked.InlineLexer = InlineLexer;
17909     marked.inlineLexer = InlineLexer.output;
17910     
17911     marked.parse = marked;
17912     
17913     Roo.Markdown.marked = marked;
17914
17915 })();/*
17916  * Based on:
17917  * Ext JS Library 1.1.1
17918  * Copyright(c) 2006-2007, Ext JS, LLC.
17919  *
17920  * Originally Released Under LGPL - original licence link has changed is not relivant.
17921  *
17922  * Fork - LGPL
17923  * <script type="text/javascript">
17924  */
17925
17926
17927
17928 /*
17929  * These classes are derivatives of the similarly named classes in the YUI Library.
17930  * The original license:
17931  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17932  * Code licensed under the BSD License:
17933  * http://developer.yahoo.net/yui/license.txt
17934  */
17935
17936 (function() {
17937
17938 var Event=Roo.EventManager;
17939 var Dom=Roo.lib.Dom;
17940
17941 /**
17942  * @class Roo.dd.DragDrop
17943  * @extends Roo.util.Observable
17944  * Defines the interface and base operation of items that that can be
17945  * dragged or can be drop targets.  It was designed to be extended, overriding
17946  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17947  * Up to three html elements can be associated with a DragDrop instance:
17948  * <ul>
17949  * <li>linked element: the element that is passed into the constructor.
17950  * This is the element which defines the boundaries for interaction with
17951  * other DragDrop objects.</li>
17952  * <li>handle element(s): The drag operation only occurs if the element that
17953  * was clicked matches a handle element.  By default this is the linked
17954  * element, but there are times that you will want only a portion of the
17955  * linked element to initiate the drag operation, and the setHandleElId()
17956  * method provides a way to define this.</li>
17957  * <li>drag element: this represents the element that would be moved along
17958  * with the cursor during a drag operation.  By default, this is the linked
17959  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17960  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17961  * </li>
17962  * </ul>
17963  * This class should not be instantiated until the onload event to ensure that
17964  * the associated elements are available.
17965  * The following would define a DragDrop obj that would interact with any
17966  * other DragDrop obj in the "group1" group:
17967  * <pre>
17968  *  dd = new Roo.dd.DragDrop("div1", "group1");
17969  * </pre>
17970  * Since none of the event handlers have been implemented, nothing would
17971  * actually happen if you were to run the code above.  Normally you would
17972  * override this class or one of the default implementations, but you can
17973  * also override the methods you want on an instance of the class...
17974  * <pre>
17975  *  dd.onDragDrop = function(e, id) {
17976  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
17977  *  }
17978  * </pre>
17979  * @constructor
17980  * @param {String} id of the element that is linked to this instance
17981  * @param {String} sGroup the group of related DragDrop objects
17982  * @param {object} config an object containing configurable attributes
17983  *                Valid properties for DragDrop:
17984  *                    padding, isTarget, maintainOffset, primaryButtonOnly
17985  */
17986 Roo.dd.DragDrop = function(id, sGroup, config) {
17987     if (id) {
17988         this.init(id, sGroup, config);
17989     }
17990     
17991 };
17992
17993 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
17994
17995     /**
17996      * The id of the element associated with this object.  This is what we
17997      * refer to as the "linked element" because the size and position of
17998      * this element is used to determine when the drag and drop objects have
17999      * interacted.
18000      * @property id
18001      * @type String
18002      */
18003     id: null,
18004
18005     /**
18006      * Configuration attributes passed into the constructor
18007      * @property config
18008      * @type object
18009      */
18010     config: null,
18011
18012     /**
18013      * The id of the element that will be dragged.  By default this is same
18014      * as the linked element , but could be changed to another element. Ex:
18015      * Roo.dd.DDProxy
18016      * @property dragElId
18017      * @type String
18018      * @private
18019      */
18020     dragElId: null,
18021
18022     /**
18023      * the id of the element that initiates the drag operation.  By default
18024      * this is the linked element, but could be changed to be a child of this
18025      * element.  This lets us do things like only starting the drag when the
18026      * header element within the linked html element is clicked.
18027      * @property handleElId
18028      * @type String
18029      * @private
18030      */
18031     handleElId: null,
18032
18033     /**
18034      * An associative array of HTML tags that will be ignored if clicked.
18035      * @property invalidHandleTypes
18036      * @type {string: string}
18037      */
18038     invalidHandleTypes: null,
18039
18040     /**
18041      * An associative array of ids for elements that will be ignored if clicked
18042      * @property invalidHandleIds
18043      * @type {string: string}
18044      */
18045     invalidHandleIds: null,
18046
18047     /**
18048      * An indexted array of css class names for elements that will be ignored
18049      * if clicked.
18050      * @property invalidHandleClasses
18051      * @type string[]
18052      */
18053     invalidHandleClasses: null,
18054
18055     /**
18056      * The linked element's absolute X position at the time the drag was
18057      * started
18058      * @property startPageX
18059      * @type int
18060      * @private
18061      */
18062     startPageX: 0,
18063
18064     /**
18065      * The linked element's absolute X position at the time the drag was
18066      * started
18067      * @property startPageY
18068      * @type int
18069      * @private
18070      */
18071     startPageY: 0,
18072
18073     /**
18074      * The group defines a logical collection of DragDrop objects that are
18075      * related.  Instances only get events when interacting with other
18076      * DragDrop object in the same group.  This lets us define multiple
18077      * groups using a single DragDrop subclass if we want.
18078      * @property groups
18079      * @type {string: string}
18080      */
18081     groups: null,
18082
18083     /**
18084      * Individual drag/drop instances can be locked.  This will prevent
18085      * onmousedown start drag.
18086      * @property locked
18087      * @type boolean
18088      * @private
18089      */
18090     locked: false,
18091
18092     /**
18093      * Lock this instance
18094      * @method lock
18095      */
18096     lock: function() { this.locked = true; },
18097
18098     /**
18099      * Unlock this instace
18100      * @method unlock
18101      */
18102     unlock: function() { this.locked = false; },
18103
18104     /**
18105      * By default, all insances can be a drop target.  This can be disabled by
18106      * setting isTarget to false.
18107      * @method isTarget
18108      * @type boolean
18109      */
18110     isTarget: true,
18111
18112     /**
18113      * The padding configured for this drag and drop object for calculating
18114      * the drop zone intersection with this object.
18115      * @method padding
18116      * @type int[]
18117      */
18118     padding: null,
18119
18120     /**
18121      * Cached reference to the linked element
18122      * @property _domRef
18123      * @private
18124      */
18125     _domRef: null,
18126
18127     /**
18128      * Internal typeof flag
18129      * @property __ygDragDrop
18130      * @private
18131      */
18132     __ygDragDrop: true,
18133
18134     /**
18135      * Set to true when horizontal contraints are applied
18136      * @property constrainX
18137      * @type boolean
18138      * @private
18139      */
18140     constrainX: false,
18141
18142     /**
18143      * Set to true when vertical contraints are applied
18144      * @property constrainY
18145      * @type boolean
18146      * @private
18147      */
18148     constrainY: false,
18149
18150     /**
18151      * The left constraint
18152      * @property minX
18153      * @type int
18154      * @private
18155      */
18156     minX: 0,
18157
18158     /**
18159      * The right constraint
18160      * @property maxX
18161      * @type int
18162      * @private
18163      */
18164     maxX: 0,
18165
18166     /**
18167      * The up constraint
18168      * @property minY
18169      * @type int
18170      * @type int
18171      * @private
18172      */
18173     minY: 0,
18174
18175     /**
18176      * The down constraint
18177      * @property maxY
18178      * @type int
18179      * @private
18180      */
18181     maxY: 0,
18182
18183     /**
18184      * Maintain offsets when we resetconstraints.  Set to true when you want
18185      * the position of the element relative to its parent to stay the same
18186      * when the page changes
18187      *
18188      * @property maintainOffset
18189      * @type boolean
18190      */
18191     maintainOffset: false,
18192
18193     /**
18194      * Array of pixel locations the element will snap to if we specified a
18195      * horizontal graduation/interval.  This array is generated automatically
18196      * when you define a tick interval.
18197      * @property xTicks
18198      * @type int[]
18199      */
18200     xTicks: null,
18201
18202     /**
18203      * Array of pixel locations the element will snap to if we specified a
18204      * vertical graduation/interval.  This array is generated automatically
18205      * when you define a tick interval.
18206      * @property yTicks
18207      * @type int[]
18208      */
18209     yTicks: null,
18210
18211     /**
18212      * By default the drag and drop instance will only respond to the primary
18213      * button click (left button for a right-handed mouse).  Set to true to
18214      * allow drag and drop to start with any mouse click that is propogated
18215      * by the browser
18216      * @property primaryButtonOnly
18217      * @type boolean
18218      */
18219     primaryButtonOnly: true,
18220
18221     /**
18222      * The availabe property is false until the linked dom element is accessible.
18223      * @property available
18224      * @type boolean
18225      */
18226     available: false,
18227
18228     /**
18229      * By default, drags can only be initiated if the mousedown occurs in the
18230      * region the linked element is.  This is done in part to work around a
18231      * bug in some browsers that mis-report the mousedown if the previous
18232      * mouseup happened outside of the window.  This property is set to true
18233      * if outer handles are defined.
18234      *
18235      * @property hasOuterHandles
18236      * @type boolean
18237      * @default false
18238      */
18239     hasOuterHandles: false,
18240
18241     /**
18242      * Code that executes immediately before the startDrag event
18243      * @method b4StartDrag
18244      * @private
18245      */
18246     b4StartDrag: function(x, y) { },
18247
18248     /**
18249      * Abstract method called after a drag/drop object is clicked
18250      * and the drag or mousedown time thresholds have beeen met.
18251      * @method startDrag
18252      * @param {int} X click location
18253      * @param {int} Y click location
18254      */
18255     startDrag: function(x, y) { /* override this */ },
18256
18257     /**
18258      * Code that executes immediately before the onDrag event
18259      * @method b4Drag
18260      * @private
18261      */
18262     b4Drag: function(e) { },
18263
18264     /**
18265      * Abstract method called during the onMouseMove event while dragging an
18266      * object.
18267      * @method onDrag
18268      * @param {Event} e the mousemove event
18269      */
18270     onDrag: function(e) { /* override this */ },
18271
18272     /**
18273      * Abstract method called when this element fist begins hovering over
18274      * another DragDrop obj
18275      * @method onDragEnter
18276      * @param {Event} e the mousemove event
18277      * @param {String|DragDrop[]} id In POINT mode, the element
18278      * id this is hovering over.  In INTERSECT mode, an array of one or more
18279      * dragdrop items being hovered over.
18280      */
18281     onDragEnter: function(e, id) { /* override this */ },
18282
18283     /**
18284      * Code that executes immediately before the onDragOver event
18285      * @method b4DragOver
18286      * @private
18287      */
18288     b4DragOver: function(e) { },
18289
18290     /**
18291      * Abstract method called when this element is hovering over another
18292      * DragDrop obj
18293      * @method onDragOver
18294      * @param {Event} e the mousemove event
18295      * @param {String|DragDrop[]} id In POINT mode, the element
18296      * id this is hovering over.  In INTERSECT mode, an array of dd items
18297      * being hovered over.
18298      */
18299     onDragOver: function(e, id) { /* override this */ },
18300
18301     /**
18302      * Code that executes immediately before the onDragOut event
18303      * @method b4DragOut
18304      * @private
18305      */
18306     b4DragOut: function(e) { },
18307
18308     /**
18309      * Abstract method called when we are no longer hovering over an element
18310      * @method onDragOut
18311      * @param {Event} e the mousemove event
18312      * @param {String|DragDrop[]} id In POINT mode, the element
18313      * id this was hovering over.  In INTERSECT mode, an array of dd items
18314      * that the mouse is no longer over.
18315      */
18316     onDragOut: function(e, id) { /* override this */ },
18317
18318     /**
18319      * Code that executes immediately before the onDragDrop event
18320      * @method b4DragDrop
18321      * @private
18322      */
18323     b4DragDrop: function(e) { },
18324
18325     /**
18326      * Abstract method called when this item is dropped on another DragDrop
18327      * obj
18328      * @method onDragDrop
18329      * @param {Event} e the mouseup event
18330      * @param {String|DragDrop[]} id In POINT mode, the element
18331      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18332      * was dropped on.
18333      */
18334     onDragDrop: function(e, id) { /* override this */ },
18335
18336     /**
18337      * Abstract method called when this item is dropped on an area with no
18338      * drop target
18339      * @method onInvalidDrop
18340      * @param {Event} e the mouseup event
18341      */
18342     onInvalidDrop: function(e) { /* override this */ },
18343
18344     /**
18345      * Code that executes immediately before the endDrag event
18346      * @method b4EndDrag
18347      * @private
18348      */
18349     b4EndDrag: function(e) { },
18350
18351     /**
18352      * Fired when we are done dragging the object
18353      * @method endDrag
18354      * @param {Event} e the mouseup event
18355      */
18356     endDrag: function(e) { /* override this */ },
18357
18358     /**
18359      * Code executed immediately before the onMouseDown event
18360      * @method b4MouseDown
18361      * @param {Event} e the mousedown event
18362      * @private
18363      */
18364     b4MouseDown: function(e) {  },
18365
18366     /**
18367      * Event handler that fires when a drag/drop obj gets a mousedown
18368      * @method onMouseDown
18369      * @param {Event} e the mousedown event
18370      */
18371     onMouseDown: function(e) { /* override this */ },
18372
18373     /**
18374      * Event handler that fires when a drag/drop obj gets a mouseup
18375      * @method onMouseUp
18376      * @param {Event} e the mouseup event
18377      */
18378     onMouseUp: function(e) { /* override this */ },
18379
18380     /**
18381      * Override the onAvailable method to do what is needed after the initial
18382      * position was determined.
18383      * @method onAvailable
18384      */
18385     onAvailable: function () {
18386     },
18387
18388     /*
18389      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18390      * @type Object
18391      */
18392     defaultPadding : {left:0, right:0, top:0, bottom:0},
18393
18394     /*
18395      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18396  *
18397  * Usage:
18398  <pre><code>
18399  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18400                 { dragElId: "existingProxyDiv" });
18401  dd.startDrag = function(){
18402      this.constrainTo("parent-id");
18403  };
18404  </code></pre>
18405  * Or you can initalize it using the {@link Roo.Element} object:
18406  <pre><code>
18407  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18408      startDrag : function(){
18409          this.constrainTo("parent-id");
18410      }
18411  });
18412  </code></pre>
18413      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18414      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18415      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18416      * an object containing the sides to pad. For example: {right:10, bottom:10}
18417      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18418      */
18419     constrainTo : function(constrainTo, pad, inContent){
18420         if(typeof pad == "number"){
18421             pad = {left: pad, right:pad, top:pad, bottom:pad};
18422         }
18423         pad = pad || this.defaultPadding;
18424         var b = Roo.get(this.getEl()).getBox();
18425         var ce = Roo.get(constrainTo);
18426         var s = ce.getScroll();
18427         var c, cd = ce.dom;
18428         if(cd == document.body){
18429             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18430         }else{
18431             xy = ce.getXY();
18432             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18433         }
18434
18435
18436         var topSpace = b.y - c.y;
18437         var leftSpace = b.x - c.x;
18438
18439         this.resetConstraints();
18440         this.setXConstraint(leftSpace - (pad.left||0), // left
18441                 c.width - leftSpace - b.width - (pad.right||0) //right
18442         );
18443         this.setYConstraint(topSpace - (pad.top||0), //top
18444                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18445         );
18446     },
18447
18448     /**
18449      * Returns a reference to the linked element
18450      * @method getEl
18451      * @return {HTMLElement} the html element
18452      */
18453     getEl: function() {
18454         if (!this._domRef) {
18455             this._domRef = Roo.getDom(this.id);
18456         }
18457
18458         return this._domRef;
18459     },
18460
18461     /**
18462      * Returns a reference to the actual element to drag.  By default this is
18463      * the same as the html element, but it can be assigned to another
18464      * element. An example of this can be found in Roo.dd.DDProxy
18465      * @method getDragEl
18466      * @return {HTMLElement} the html element
18467      */
18468     getDragEl: function() {
18469         return Roo.getDom(this.dragElId);
18470     },
18471
18472     /**
18473      * Sets up the DragDrop object.  Must be called in the constructor of any
18474      * Roo.dd.DragDrop subclass
18475      * @method init
18476      * @param id the id of the linked element
18477      * @param {String} sGroup the group of related items
18478      * @param {object} config configuration attributes
18479      */
18480     init: function(id, sGroup, config) {
18481         this.initTarget(id, sGroup, config);
18482         if (!Roo.isTouch) {
18483             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18484         }
18485         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18486         // Event.on(this.id, "selectstart", Event.preventDefault);
18487     },
18488
18489     /**
18490      * Initializes Targeting functionality only... the object does not
18491      * get a mousedown handler.
18492      * @method initTarget
18493      * @param id the id of the linked element
18494      * @param {String} sGroup the group of related items
18495      * @param {object} config configuration attributes
18496      */
18497     initTarget: function(id, sGroup, config) {
18498
18499         // configuration attributes
18500         this.config = config || {};
18501
18502         // create a local reference to the drag and drop manager
18503         this.DDM = Roo.dd.DDM;
18504         // initialize the groups array
18505         this.groups = {};
18506
18507         // assume that we have an element reference instead of an id if the
18508         // parameter is not a string
18509         if (typeof id !== "string") {
18510             id = Roo.id(id);
18511         }
18512
18513         // set the id
18514         this.id = id;
18515
18516         // add to an interaction group
18517         this.addToGroup((sGroup) ? sGroup : "default");
18518
18519         // We don't want to register this as the handle with the manager
18520         // so we just set the id rather than calling the setter.
18521         this.handleElId = id;
18522
18523         // the linked element is the element that gets dragged by default
18524         this.setDragElId(id);
18525
18526         // by default, clicked anchors will not start drag operations.
18527         this.invalidHandleTypes = { A: "A" };
18528         this.invalidHandleIds = {};
18529         this.invalidHandleClasses = [];
18530
18531         this.applyConfig();
18532
18533         this.handleOnAvailable();
18534     },
18535
18536     /**
18537      * Applies the configuration parameters that were passed into the constructor.
18538      * This is supposed to happen at each level through the inheritance chain.  So
18539      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18540      * DragDrop in order to get all of the parameters that are available in
18541      * each object.
18542      * @method applyConfig
18543      */
18544     applyConfig: function() {
18545
18546         // configurable properties:
18547         //    padding, isTarget, maintainOffset, primaryButtonOnly
18548         this.padding           = this.config.padding || [0, 0, 0, 0];
18549         this.isTarget          = (this.config.isTarget !== false);
18550         this.maintainOffset    = (this.config.maintainOffset);
18551         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18552
18553     },
18554
18555     /**
18556      * Executed when the linked element is available
18557      * @method handleOnAvailable
18558      * @private
18559      */
18560     handleOnAvailable: function() {
18561         this.available = true;
18562         this.resetConstraints();
18563         this.onAvailable();
18564     },
18565
18566      /**
18567      * Configures the padding for the target zone in px.  Effectively expands
18568      * (or reduces) the virtual object size for targeting calculations.
18569      * Supports css-style shorthand; if only one parameter is passed, all sides
18570      * will have that padding, and if only two are passed, the top and bottom
18571      * will have the first param, the left and right the second.
18572      * @method setPadding
18573      * @param {int} iTop    Top pad
18574      * @param {int} iRight  Right pad
18575      * @param {int} iBot    Bot pad
18576      * @param {int} iLeft   Left pad
18577      */
18578     setPadding: function(iTop, iRight, iBot, iLeft) {
18579         // this.padding = [iLeft, iRight, iTop, iBot];
18580         if (!iRight && 0 !== iRight) {
18581             this.padding = [iTop, iTop, iTop, iTop];
18582         } else if (!iBot && 0 !== iBot) {
18583             this.padding = [iTop, iRight, iTop, iRight];
18584         } else {
18585             this.padding = [iTop, iRight, iBot, iLeft];
18586         }
18587     },
18588
18589     /**
18590      * Stores the initial placement of the linked element.
18591      * @method setInitialPosition
18592      * @param {int} diffX   the X offset, default 0
18593      * @param {int} diffY   the Y offset, default 0
18594      */
18595     setInitPosition: function(diffX, diffY) {
18596         var el = this.getEl();
18597
18598         if (!this.DDM.verifyEl(el)) {
18599             return;
18600         }
18601
18602         var dx = diffX || 0;
18603         var dy = diffY || 0;
18604
18605         var p = Dom.getXY( el );
18606
18607         this.initPageX = p[0] - dx;
18608         this.initPageY = p[1] - dy;
18609
18610         this.lastPageX = p[0];
18611         this.lastPageY = p[1];
18612
18613
18614         this.setStartPosition(p);
18615     },
18616
18617     /**
18618      * Sets the start position of the element.  This is set when the obj
18619      * is initialized, the reset when a drag is started.
18620      * @method setStartPosition
18621      * @param pos current position (from previous lookup)
18622      * @private
18623      */
18624     setStartPosition: function(pos) {
18625         var p = pos || Dom.getXY( this.getEl() );
18626         this.deltaSetXY = null;
18627
18628         this.startPageX = p[0];
18629         this.startPageY = p[1];
18630     },
18631
18632     /**
18633      * Add this instance to a group of related drag/drop objects.  All
18634      * instances belong to at least one group, and can belong to as many
18635      * groups as needed.
18636      * @method addToGroup
18637      * @param sGroup {string} the name of the group
18638      */
18639     addToGroup: function(sGroup) {
18640         this.groups[sGroup] = true;
18641         this.DDM.regDragDrop(this, sGroup);
18642     },
18643
18644     /**
18645      * Remove's this instance from the supplied interaction group
18646      * @method removeFromGroup
18647      * @param {string}  sGroup  The group to drop
18648      */
18649     removeFromGroup: function(sGroup) {
18650         if (this.groups[sGroup]) {
18651             delete this.groups[sGroup];
18652         }
18653
18654         this.DDM.removeDDFromGroup(this, sGroup);
18655     },
18656
18657     /**
18658      * Allows you to specify that an element other than the linked element
18659      * will be moved with the cursor during a drag
18660      * @method setDragElId
18661      * @param id {string} the id of the element that will be used to initiate the drag
18662      */
18663     setDragElId: function(id) {
18664         this.dragElId = id;
18665     },
18666
18667     /**
18668      * Allows you to specify a child of the linked element that should be
18669      * used to initiate the drag operation.  An example of this would be if
18670      * you have a content div with text and links.  Clicking anywhere in the
18671      * content area would normally start the drag operation.  Use this method
18672      * to specify that an element inside of the content div is the element
18673      * that starts the drag operation.
18674      * @method setHandleElId
18675      * @param id {string} the id of the element that will be used to
18676      * initiate the drag.
18677      */
18678     setHandleElId: function(id) {
18679         if (typeof id !== "string") {
18680             id = Roo.id(id);
18681         }
18682         this.handleElId = id;
18683         this.DDM.regHandle(this.id, id);
18684     },
18685
18686     /**
18687      * Allows you to set an element outside of the linked element as a drag
18688      * handle
18689      * @method setOuterHandleElId
18690      * @param id the id of the element that will be used to initiate the drag
18691      */
18692     setOuterHandleElId: function(id) {
18693         if (typeof id !== "string") {
18694             id = Roo.id(id);
18695         }
18696         Event.on(id, "mousedown",
18697                 this.handleMouseDown, this);
18698         this.setHandleElId(id);
18699
18700         this.hasOuterHandles = true;
18701     },
18702
18703     /**
18704      * Remove all drag and drop hooks for this element
18705      * @method unreg
18706      */
18707     unreg: function() {
18708         Event.un(this.id, "mousedown",
18709                 this.handleMouseDown);
18710         Event.un(this.id, "touchstart",
18711                 this.handleMouseDown);
18712         this._domRef = null;
18713         this.DDM._remove(this);
18714     },
18715
18716     destroy : function(){
18717         this.unreg();
18718     },
18719
18720     /**
18721      * Returns true if this instance is locked, or the drag drop mgr is locked
18722      * (meaning that all drag/drop is disabled on the page.)
18723      * @method isLocked
18724      * @return {boolean} true if this obj or all drag/drop is locked, else
18725      * false
18726      */
18727     isLocked: function() {
18728         return (this.DDM.isLocked() || this.locked);
18729     },
18730
18731     /**
18732      * Fired when this object is clicked
18733      * @method handleMouseDown
18734      * @param {Event} e
18735      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18736      * @private
18737      */
18738     handleMouseDown: function(e, oDD){
18739      
18740         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18741             //Roo.log('not touch/ button !=0');
18742             return;
18743         }
18744         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18745             return; // double touch..
18746         }
18747         
18748
18749         if (this.isLocked()) {
18750             //Roo.log('locked');
18751             return;
18752         }
18753
18754         this.DDM.refreshCache(this.groups);
18755 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18756         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18757         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18758             //Roo.log('no outer handes or not over target');
18759                 // do nothing.
18760         } else {
18761 //            Roo.log('check validator');
18762             if (this.clickValidator(e)) {
18763 //                Roo.log('validate success');
18764                 // set the initial element position
18765                 this.setStartPosition();
18766
18767
18768                 this.b4MouseDown(e);
18769                 this.onMouseDown(e);
18770
18771                 this.DDM.handleMouseDown(e, this);
18772
18773                 this.DDM.stopEvent(e);
18774             } else {
18775
18776
18777             }
18778         }
18779     },
18780
18781     clickValidator: function(e) {
18782         var target = e.getTarget();
18783         return ( this.isValidHandleChild(target) &&
18784                     (this.id == this.handleElId ||
18785                         this.DDM.handleWasClicked(target, this.id)) );
18786     },
18787
18788     /**
18789      * Allows you to specify a tag name that should not start a drag operation
18790      * when clicked.  This is designed to facilitate embedding links within a
18791      * drag handle that do something other than start the drag.
18792      * @method addInvalidHandleType
18793      * @param {string} tagName the type of element to exclude
18794      */
18795     addInvalidHandleType: function(tagName) {
18796         var type = tagName.toUpperCase();
18797         this.invalidHandleTypes[type] = type;
18798     },
18799
18800     /**
18801      * Lets you to specify an element id for a child of a drag handle
18802      * that should not initiate a drag
18803      * @method addInvalidHandleId
18804      * @param {string} id the element id of the element you wish to ignore
18805      */
18806     addInvalidHandleId: function(id) {
18807         if (typeof id !== "string") {
18808             id = Roo.id(id);
18809         }
18810         this.invalidHandleIds[id] = id;
18811     },
18812
18813     /**
18814      * Lets you specify a css class of elements that will not initiate a drag
18815      * @method addInvalidHandleClass
18816      * @param {string} cssClass the class of the elements you wish to ignore
18817      */
18818     addInvalidHandleClass: function(cssClass) {
18819         this.invalidHandleClasses.push(cssClass);
18820     },
18821
18822     /**
18823      * Unsets an excluded tag name set by addInvalidHandleType
18824      * @method removeInvalidHandleType
18825      * @param {string} tagName the type of element to unexclude
18826      */
18827     removeInvalidHandleType: function(tagName) {
18828         var type = tagName.toUpperCase();
18829         // this.invalidHandleTypes[type] = null;
18830         delete this.invalidHandleTypes[type];
18831     },
18832
18833     /**
18834      * Unsets an invalid handle id
18835      * @method removeInvalidHandleId
18836      * @param {string} id the id of the element to re-enable
18837      */
18838     removeInvalidHandleId: function(id) {
18839         if (typeof id !== "string") {
18840             id = Roo.id(id);
18841         }
18842         delete this.invalidHandleIds[id];
18843     },
18844
18845     /**
18846      * Unsets an invalid css class
18847      * @method removeInvalidHandleClass
18848      * @param {string} cssClass the class of the element(s) you wish to
18849      * re-enable
18850      */
18851     removeInvalidHandleClass: function(cssClass) {
18852         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18853             if (this.invalidHandleClasses[i] == cssClass) {
18854                 delete this.invalidHandleClasses[i];
18855             }
18856         }
18857     },
18858
18859     /**
18860      * Checks the tag exclusion list to see if this click should be ignored
18861      * @method isValidHandleChild
18862      * @param {HTMLElement} node the HTMLElement to evaluate
18863      * @return {boolean} true if this is a valid tag type, false if not
18864      */
18865     isValidHandleChild: function(node) {
18866
18867         var valid = true;
18868         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18869         var nodeName;
18870         try {
18871             nodeName = node.nodeName.toUpperCase();
18872         } catch(e) {
18873             nodeName = node.nodeName;
18874         }
18875         valid = valid && !this.invalidHandleTypes[nodeName];
18876         valid = valid && !this.invalidHandleIds[node.id];
18877
18878         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18879             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18880         }
18881
18882
18883         return valid;
18884
18885     },
18886
18887     /**
18888      * Create the array of horizontal tick marks if an interval was specified
18889      * in setXConstraint().
18890      * @method setXTicks
18891      * @private
18892      */
18893     setXTicks: function(iStartX, iTickSize) {
18894         this.xTicks = [];
18895         this.xTickSize = iTickSize;
18896
18897         var tickMap = {};
18898
18899         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18900             if (!tickMap[i]) {
18901                 this.xTicks[this.xTicks.length] = i;
18902                 tickMap[i] = true;
18903             }
18904         }
18905
18906         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18907             if (!tickMap[i]) {
18908                 this.xTicks[this.xTicks.length] = i;
18909                 tickMap[i] = true;
18910             }
18911         }
18912
18913         this.xTicks.sort(this.DDM.numericSort) ;
18914     },
18915
18916     /**
18917      * Create the array of vertical tick marks if an interval was specified in
18918      * setYConstraint().
18919      * @method setYTicks
18920      * @private
18921      */
18922     setYTicks: function(iStartY, iTickSize) {
18923         this.yTicks = [];
18924         this.yTickSize = iTickSize;
18925
18926         var tickMap = {};
18927
18928         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18929             if (!tickMap[i]) {
18930                 this.yTicks[this.yTicks.length] = i;
18931                 tickMap[i] = true;
18932             }
18933         }
18934
18935         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18936             if (!tickMap[i]) {
18937                 this.yTicks[this.yTicks.length] = i;
18938                 tickMap[i] = true;
18939             }
18940         }
18941
18942         this.yTicks.sort(this.DDM.numericSort) ;
18943     },
18944
18945     /**
18946      * By default, the element can be dragged any place on the screen.  Use
18947      * this method to limit the horizontal travel of the element.  Pass in
18948      * 0,0 for the parameters if you want to lock the drag to the y axis.
18949      * @method setXConstraint
18950      * @param {int} iLeft the number of pixels the element can move to the left
18951      * @param {int} iRight the number of pixels the element can move to the
18952      * right
18953      * @param {int} iTickSize optional parameter for specifying that the
18954      * element
18955      * should move iTickSize pixels at a time.
18956      */
18957     setXConstraint: function(iLeft, iRight, iTickSize) {
18958         this.leftConstraint = iLeft;
18959         this.rightConstraint = iRight;
18960
18961         this.minX = this.initPageX - iLeft;
18962         this.maxX = this.initPageX + iRight;
18963         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18964
18965         this.constrainX = true;
18966     },
18967
18968     /**
18969      * Clears any constraints applied to this instance.  Also clears ticks
18970      * since they can't exist independent of a constraint at this time.
18971      * @method clearConstraints
18972      */
18973     clearConstraints: function() {
18974         this.constrainX = false;
18975         this.constrainY = false;
18976         this.clearTicks();
18977     },
18978
18979     /**
18980      * Clears any tick interval defined for this instance
18981      * @method clearTicks
18982      */
18983     clearTicks: function() {
18984         this.xTicks = null;
18985         this.yTicks = null;
18986         this.xTickSize = 0;
18987         this.yTickSize = 0;
18988     },
18989
18990     /**
18991      * By default, the element can be dragged any place on the screen.  Set
18992      * this to limit the vertical travel of the element.  Pass in 0,0 for the
18993      * parameters if you want to lock the drag to the x axis.
18994      * @method setYConstraint
18995      * @param {int} iUp the number of pixels the element can move up
18996      * @param {int} iDown the number of pixels the element can move down
18997      * @param {int} iTickSize optional parameter for specifying that the
18998      * element should move iTickSize pixels at a time.
18999      */
19000     setYConstraint: function(iUp, iDown, iTickSize) {
19001         this.topConstraint = iUp;
19002         this.bottomConstraint = iDown;
19003
19004         this.minY = this.initPageY - iUp;
19005         this.maxY = this.initPageY + iDown;
19006         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19007
19008         this.constrainY = true;
19009
19010     },
19011
19012     /**
19013      * resetConstraints must be called if you manually reposition a dd element.
19014      * @method resetConstraints
19015      * @param {boolean} maintainOffset
19016      */
19017     resetConstraints: function() {
19018
19019
19020         // Maintain offsets if necessary
19021         if (this.initPageX || this.initPageX === 0) {
19022             // figure out how much this thing has moved
19023             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19024             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19025
19026             this.setInitPosition(dx, dy);
19027
19028         // This is the first time we have detected the element's position
19029         } else {
19030             this.setInitPosition();
19031         }
19032
19033         if (this.constrainX) {
19034             this.setXConstraint( this.leftConstraint,
19035                                  this.rightConstraint,
19036                                  this.xTickSize        );
19037         }
19038
19039         if (this.constrainY) {
19040             this.setYConstraint( this.topConstraint,
19041                                  this.bottomConstraint,
19042                                  this.yTickSize         );
19043         }
19044     },
19045
19046     /**
19047      * Normally the drag element is moved pixel by pixel, but we can specify
19048      * that it move a number of pixels at a time.  This method resolves the
19049      * location when we have it set up like this.
19050      * @method getTick
19051      * @param {int} val where we want to place the object
19052      * @param {int[]} tickArray sorted array of valid points
19053      * @return {int} the closest tick
19054      * @private
19055      */
19056     getTick: function(val, tickArray) {
19057
19058         if (!tickArray) {
19059             // If tick interval is not defined, it is effectively 1 pixel,
19060             // so we return the value passed to us.
19061             return val;
19062         } else if (tickArray[0] >= val) {
19063             // The value is lower than the first tick, so we return the first
19064             // tick.
19065             return tickArray[0];
19066         } else {
19067             for (var i=0, len=tickArray.length; i<len; ++i) {
19068                 var next = i + 1;
19069                 if (tickArray[next] && tickArray[next] >= val) {
19070                     var diff1 = val - tickArray[i];
19071                     var diff2 = tickArray[next] - val;
19072                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19073                 }
19074             }
19075
19076             // The value is larger than the last tick, so we return the last
19077             // tick.
19078             return tickArray[tickArray.length - 1];
19079         }
19080     },
19081
19082     /**
19083      * toString method
19084      * @method toString
19085      * @return {string} string representation of the dd obj
19086      */
19087     toString: function() {
19088         return ("DragDrop " + this.id);
19089     }
19090
19091 });
19092
19093 })();
19094 /*
19095  * Based on:
19096  * Ext JS Library 1.1.1
19097  * Copyright(c) 2006-2007, Ext JS, LLC.
19098  *
19099  * Originally Released Under LGPL - original licence link has changed is not relivant.
19100  *
19101  * Fork - LGPL
19102  * <script type="text/javascript">
19103  */
19104
19105
19106 /**
19107  * The drag and drop utility provides a framework for building drag and drop
19108  * applications.  In addition to enabling drag and drop for specific elements,
19109  * the drag and drop elements are tracked by the manager class, and the
19110  * interactions between the various elements are tracked during the drag and
19111  * the implementing code is notified about these important moments.
19112  */
19113
19114 // Only load the library once.  Rewriting the manager class would orphan
19115 // existing drag and drop instances.
19116 if (!Roo.dd.DragDropMgr) {
19117
19118 /**
19119  * @class Roo.dd.DragDropMgr
19120  * DragDropMgr is a singleton that tracks the element interaction for
19121  * all DragDrop items in the window.  Generally, you will not call
19122  * this class directly, but it does have helper methods that could
19123  * be useful in your DragDrop implementations.
19124  * @singleton
19125  */
19126 Roo.dd.DragDropMgr = function() {
19127
19128     var Event = Roo.EventManager;
19129
19130     return {
19131
19132         /**
19133          * Two dimensional Array of registered DragDrop objects.  The first
19134          * dimension is the DragDrop item group, the second the DragDrop
19135          * object.
19136          * @property ids
19137          * @type {string: string}
19138          * @private
19139          * @static
19140          */
19141         ids: {},
19142
19143         /**
19144          * Array of element ids defined as drag handles.  Used to determine
19145          * if the element that generated the mousedown event is actually the
19146          * handle and not the html element itself.
19147          * @property handleIds
19148          * @type {string: string}
19149          * @private
19150          * @static
19151          */
19152         handleIds: {},
19153
19154         /**
19155          * the DragDrop object that is currently being dragged
19156          * @property dragCurrent
19157          * @type DragDrop
19158          * @private
19159          * @static
19160          **/
19161         dragCurrent: null,
19162
19163         /**
19164          * the DragDrop object(s) that are being hovered over
19165          * @property dragOvers
19166          * @type Array
19167          * @private
19168          * @static
19169          */
19170         dragOvers: {},
19171
19172         /**
19173          * the X distance between the cursor and the object being dragged
19174          * @property deltaX
19175          * @type int
19176          * @private
19177          * @static
19178          */
19179         deltaX: 0,
19180
19181         /**
19182          * the Y distance between the cursor and the object being dragged
19183          * @property deltaY
19184          * @type int
19185          * @private
19186          * @static
19187          */
19188         deltaY: 0,
19189
19190         /**
19191          * Flag to determine if we should prevent the default behavior of the
19192          * events we define. By default this is true, but this can be set to
19193          * false if you need the default behavior (not recommended)
19194          * @property preventDefault
19195          * @type boolean
19196          * @static
19197          */
19198         preventDefault: true,
19199
19200         /**
19201          * Flag to determine if we should stop the propagation of the events
19202          * we generate. This is true by default but you may want to set it to
19203          * false if the html element contains other features that require the
19204          * mouse click.
19205          * @property stopPropagation
19206          * @type boolean
19207          * @static
19208          */
19209         stopPropagation: true,
19210
19211         /**
19212          * Internal flag that is set to true when drag and drop has been
19213          * intialized
19214          * @property initialized
19215          * @private
19216          * @static
19217          */
19218         initalized: false,
19219
19220         /**
19221          * All drag and drop can be disabled.
19222          * @property locked
19223          * @private
19224          * @static
19225          */
19226         locked: false,
19227
19228         /**
19229          * Called the first time an element is registered.
19230          * @method init
19231          * @private
19232          * @static
19233          */
19234         init: function() {
19235             this.initialized = true;
19236         },
19237
19238         /**
19239          * In point mode, drag and drop interaction is defined by the
19240          * location of the cursor during the drag/drop
19241          * @property POINT
19242          * @type int
19243          * @static
19244          */
19245         POINT: 0,
19246
19247         /**
19248          * In intersect mode, drag and drop interactio nis defined by the
19249          * overlap of two or more drag and drop objects.
19250          * @property INTERSECT
19251          * @type int
19252          * @static
19253          */
19254         INTERSECT: 1,
19255
19256         /**
19257          * The current drag and drop mode.  Default: POINT
19258          * @property mode
19259          * @type int
19260          * @static
19261          */
19262         mode: 0,
19263
19264         /**
19265          * Runs method on all drag and drop objects
19266          * @method _execOnAll
19267          * @private
19268          * @static
19269          */
19270         _execOnAll: function(sMethod, args) {
19271             for (var i in this.ids) {
19272                 for (var j in this.ids[i]) {
19273                     var oDD = this.ids[i][j];
19274                     if (! this.isTypeOfDD(oDD)) {
19275                         continue;
19276                     }
19277                     oDD[sMethod].apply(oDD, args);
19278                 }
19279             }
19280         },
19281
19282         /**
19283          * Drag and drop initialization.  Sets up the global event handlers
19284          * @method _onLoad
19285          * @private
19286          * @static
19287          */
19288         _onLoad: function() {
19289
19290             this.init();
19291
19292             if (!Roo.isTouch) {
19293                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19294                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19295             }
19296             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19297             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19298             
19299             Event.on(window,   "unload",    this._onUnload, this, true);
19300             Event.on(window,   "resize",    this._onResize, this, true);
19301             // Event.on(window,   "mouseout",    this._test);
19302
19303         },
19304
19305         /**
19306          * Reset constraints on all drag and drop objs
19307          * @method _onResize
19308          * @private
19309          * @static
19310          */
19311         _onResize: function(e) {
19312             this._execOnAll("resetConstraints", []);
19313         },
19314
19315         /**
19316          * Lock all drag and drop functionality
19317          * @method lock
19318          * @static
19319          */
19320         lock: function() { this.locked = true; },
19321
19322         /**
19323          * Unlock all drag and drop functionality
19324          * @method unlock
19325          * @static
19326          */
19327         unlock: function() { this.locked = false; },
19328
19329         /**
19330          * Is drag and drop locked?
19331          * @method isLocked
19332          * @return {boolean} True if drag and drop is locked, false otherwise.
19333          * @static
19334          */
19335         isLocked: function() { return this.locked; },
19336
19337         /**
19338          * Location cache that is set for all drag drop objects when a drag is
19339          * initiated, cleared when the drag is finished.
19340          * @property locationCache
19341          * @private
19342          * @static
19343          */
19344         locationCache: {},
19345
19346         /**
19347          * Set useCache to false if you want to force object the lookup of each
19348          * drag and drop linked element constantly during a drag.
19349          * @property useCache
19350          * @type boolean
19351          * @static
19352          */
19353         useCache: true,
19354
19355         /**
19356          * The number of pixels that the mouse needs to move after the
19357          * mousedown before the drag is initiated.  Default=3;
19358          * @property clickPixelThresh
19359          * @type int
19360          * @static
19361          */
19362         clickPixelThresh: 3,
19363
19364         /**
19365          * The number of milliseconds after the mousedown event to initiate the
19366          * drag if we don't get a mouseup event. Default=1000
19367          * @property clickTimeThresh
19368          * @type int
19369          * @static
19370          */
19371         clickTimeThresh: 350,
19372
19373         /**
19374          * Flag that indicates that either the drag pixel threshold or the
19375          * mousdown time threshold has been met
19376          * @property dragThreshMet
19377          * @type boolean
19378          * @private
19379          * @static
19380          */
19381         dragThreshMet: false,
19382
19383         /**
19384          * Timeout used for the click time threshold
19385          * @property clickTimeout
19386          * @type Object
19387          * @private
19388          * @static
19389          */
19390         clickTimeout: null,
19391
19392         /**
19393          * The X position of the mousedown event stored for later use when a
19394          * drag threshold is met.
19395          * @property startX
19396          * @type int
19397          * @private
19398          * @static
19399          */
19400         startX: 0,
19401
19402         /**
19403          * The Y position of the mousedown event stored for later use when a
19404          * drag threshold is met.
19405          * @property startY
19406          * @type int
19407          * @private
19408          * @static
19409          */
19410         startY: 0,
19411
19412         /**
19413          * Each DragDrop instance must be registered with the DragDropMgr.
19414          * This is executed in DragDrop.init()
19415          * @method regDragDrop
19416          * @param {DragDrop} oDD the DragDrop object to register
19417          * @param {String} sGroup the name of the group this element belongs to
19418          * @static
19419          */
19420         regDragDrop: function(oDD, sGroup) {
19421             if (!this.initialized) { this.init(); }
19422
19423             if (!this.ids[sGroup]) {
19424                 this.ids[sGroup] = {};
19425             }
19426             this.ids[sGroup][oDD.id] = oDD;
19427         },
19428
19429         /**
19430          * Removes the supplied dd instance from the supplied group. Executed
19431          * by DragDrop.removeFromGroup, so don't call this function directly.
19432          * @method removeDDFromGroup
19433          * @private
19434          * @static
19435          */
19436         removeDDFromGroup: function(oDD, sGroup) {
19437             if (!this.ids[sGroup]) {
19438                 this.ids[sGroup] = {};
19439             }
19440
19441             var obj = this.ids[sGroup];
19442             if (obj && obj[oDD.id]) {
19443                 delete obj[oDD.id];
19444             }
19445         },
19446
19447         /**
19448          * Unregisters a drag and drop item.  This is executed in
19449          * DragDrop.unreg, use that method instead of calling this directly.
19450          * @method _remove
19451          * @private
19452          * @static
19453          */
19454         _remove: function(oDD) {
19455             for (var g in oDD.groups) {
19456                 if (g && this.ids[g][oDD.id]) {
19457                     delete this.ids[g][oDD.id];
19458                 }
19459             }
19460             delete this.handleIds[oDD.id];
19461         },
19462
19463         /**
19464          * Each DragDrop handle element must be registered.  This is done
19465          * automatically when executing DragDrop.setHandleElId()
19466          * @method regHandle
19467          * @param {String} sDDId the DragDrop id this element is a handle for
19468          * @param {String} sHandleId the id of the element that is the drag
19469          * handle
19470          * @static
19471          */
19472         regHandle: function(sDDId, sHandleId) {
19473             if (!this.handleIds[sDDId]) {
19474                 this.handleIds[sDDId] = {};
19475             }
19476             this.handleIds[sDDId][sHandleId] = sHandleId;
19477         },
19478
19479         /**
19480          * Utility function to determine if a given element has been
19481          * registered as a drag drop item.
19482          * @method isDragDrop
19483          * @param {String} id the element id to check
19484          * @return {boolean} true if this element is a DragDrop item,
19485          * false otherwise
19486          * @static
19487          */
19488         isDragDrop: function(id) {
19489             return ( this.getDDById(id) ) ? true : false;
19490         },
19491
19492         /**
19493          * Returns the drag and drop instances that are in all groups the
19494          * passed in instance belongs to.
19495          * @method getRelated
19496          * @param {DragDrop} p_oDD the obj to get related data for
19497          * @param {boolean} bTargetsOnly if true, only return targetable objs
19498          * @return {DragDrop[]} the related instances
19499          * @static
19500          */
19501         getRelated: function(p_oDD, bTargetsOnly) {
19502             var oDDs = [];
19503             for (var i in p_oDD.groups) {
19504                 for (j in this.ids[i]) {
19505                     var dd = this.ids[i][j];
19506                     if (! this.isTypeOfDD(dd)) {
19507                         continue;
19508                     }
19509                     if (!bTargetsOnly || dd.isTarget) {
19510                         oDDs[oDDs.length] = dd;
19511                     }
19512                 }
19513             }
19514
19515             return oDDs;
19516         },
19517
19518         /**
19519          * Returns true if the specified dd target is a legal target for
19520          * the specifice drag obj
19521          * @method isLegalTarget
19522          * @param {DragDrop} the drag obj
19523          * @param {DragDrop} the target
19524          * @return {boolean} true if the target is a legal target for the
19525          * dd obj
19526          * @static
19527          */
19528         isLegalTarget: function (oDD, oTargetDD) {
19529             var targets = this.getRelated(oDD, true);
19530             for (var i=0, len=targets.length;i<len;++i) {
19531                 if (targets[i].id == oTargetDD.id) {
19532                     return true;
19533                 }
19534             }
19535
19536             return false;
19537         },
19538
19539         /**
19540          * My goal is to be able to transparently determine if an object is
19541          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19542          * returns "object", oDD.constructor.toString() always returns
19543          * "DragDrop" and not the name of the subclass.  So for now it just
19544          * evaluates a well-known variable in DragDrop.
19545          * @method isTypeOfDD
19546          * @param {Object} the object to evaluate
19547          * @return {boolean} true if typeof oDD = DragDrop
19548          * @static
19549          */
19550         isTypeOfDD: function (oDD) {
19551             return (oDD && oDD.__ygDragDrop);
19552         },
19553
19554         /**
19555          * Utility function to determine if a given element has been
19556          * registered as a drag drop handle for the given Drag Drop object.
19557          * @method isHandle
19558          * @param {String} id the element id to check
19559          * @return {boolean} true if this element is a DragDrop handle, false
19560          * otherwise
19561          * @static
19562          */
19563         isHandle: function(sDDId, sHandleId) {
19564             return ( this.handleIds[sDDId] &&
19565                             this.handleIds[sDDId][sHandleId] );
19566         },
19567
19568         /**
19569          * Returns the DragDrop instance for a given id
19570          * @method getDDById
19571          * @param {String} id the id of the DragDrop object
19572          * @return {DragDrop} the drag drop object, null if it is not found
19573          * @static
19574          */
19575         getDDById: function(id) {
19576             for (var i in this.ids) {
19577                 if (this.ids[i][id]) {
19578                     return this.ids[i][id];
19579                 }
19580             }
19581             return null;
19582         },
19583
19584         /**
19585          * Fired after a registered DragDrop object gets the mousedown event.
19586          * Sets up the events required to track the object being dragged
19587          * @method handleMouseDown
19588          * @param {Event} e the event
19589          * @param oDD the DragDrop object being dragged
19590          * @private
19591          * @static
19592          */
19593         handleMouseDown: function(e, oDD) {
19594             if(Roo.QuickTips){
19595                 Roo.QuickTips.disable();
19596             }
19597             this.currentTarget = e.getTarget();
19598
19599             this.dragCurrent = oDD;
19600
19601             var el = oDD.getEl();
19602
19603             // track start position
19604             this.startX = e.getPageX();
19605             this.startY = e.getPageY();
19606
19607             this.deltaX = this.startX - el.offsetLeft;
19608             this.deltaY = this.startY - el.offsetTop;
19609
19610             this.dragThreshMet = false;
19611
19612             this.clickTimeout = setTimeout(
19613                     function() {
19614                         var DDM = Roo.dd.DDM;
19615                         DDM.startDrag(DDM.startX, DDM.startY);
19616                     },
19617                     this.clickTimeThresh );
19618         },
19619
19620         /**
19621          * Fired when either the drag pixel threshol or the mousedown hold
19622          * time threshold has been met.
19623          * @method startDrag
19624          * @param x {int} the X position of the original mousedown
19625          * @param y {int} the Y position of the original mousedown
19626          * @static
19627          */
19628         startDrag: function(x, y) {
19629             clearTimeout(this.clickTimeout);
19630             if (this.dragCurrent) {
19631                 this.dragCurrent.b4StartDrag(x, y);
19632                 this.dragCurrent.startDrag(x, y);
19633             }
19634             this.dragThreshMet = true;
19635         },
19636
19637         /**
19638          * Internal function to handle the mouseup event.  Will be invoked
19639          * from the context of the document.
19640          * @method handleMouseUp
19641          * @param {Event} e the event
19642          * @private
19643          * @static
19644          */
19645         handleMouseUp: function(e) {
19646
19647             if(Roo.QuickTips){
19648                 Roo.QuickTips.enable();
19649             }
19650             if (! this.dragCurrent) {
19651                 return;
19652             }
19653
19654             clearTimeout(this.clickTimeout);
19655
19656             if (this.dragThreshMet) {
19657                 this.fireEvents(e, true);
19658             } else {
19659             }
19660
19661             this.stopDrag(e);
19662
19663             this.stopEvent(e);
19664         },
19665
19666         /**
19667          * Utility to stop event propagation and event default, if these
19668          * features are turned on.
19669          * @method stopEvent
19670          * @param {Event} e the event as returned by this.getEvent()
19671          * @static
19672          */
19673         stopEvent: function(e){
19674             if(this.stopPropagation) {
19675                 e.stopPropagation();
19676             }
19677
19678             if (this.preventDefault) {
19679                 e.preventDefault();
19680             }
19681         },
19682
19683         /**
19684          * Internal function to clean up event handlers after the drag
19685          * operation is complete
19686          * @method stopDrag
19687          * @param {Event} e the event
19688          * @private
19689          * @static
19690          */
19691         stopDrag: function(e) {
19692             // Fire the drag end event for the item that was dragged
19693             if (this.dragCurrent) {
19694                 if (this.dragThreshMet) {
19695                     this.dragCurrent.b4EndDrag(e);
19696                     this.dragCurrent.endDrag(e);
19697                 }
19698
19699                 this.dragCurrent.onMouseUp(e);
19700             }
19701
19702             this.dragCurrent = null;
19703             this.dragOvers = {};
19704         },
19705
19706         /**
19707          * Internal function to handle the mousemove event.  Will be invoked
19708          * from the context of the html element.
19709          *
19710          * @TODO figure out what we can do about mouse events lost when the
19711          * user drags objects beyond the window boundary.  Currently we can
19712          * detect this in internet explorer by verifying that the mouse is
19713          * down during the mousemove event.  Firefox doesn't give us the
19714          * button state on the mousemove event.
19715          * @method handleMouseMove
19716          * @param {Event} e the event
19717          * @private
19718          * @static
19719          */
19720         handleMouseMove: function(e) {
19721             if (! this.dragCurrent) {
19722                 return true;
19723             }
19724
19725             // var button = e.which || e.button;
19726
19727             // check for IE mouseup outside of page boundary
19728             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19729                 this.stopEvent(e);
19730                 return this.handleMouseUp(e);
19731             }
19732
19733             if (!this.dragThreshMet) {
19734                 var diffX = Math.abs(this.startX - e.getPageX());
19735                 var diffY = Math.abs(this.startY - e.getPageY());
19736                 if (diffX > this.clickPixelThresh ||
19737                             diffY > this.clickPixelThresh) {
19738                     this.startDrag(this.startX, this.startY);
19739                 }
19740             }
19741
19742             if (this.dragThreshMet) {
19743                 this.dragCurrent.b4Drag(e);
19744                 this.dragCurrent.onDrag(e);
19745                 if(!this.dragCurrent.moveOnly){
19746                     this.fireEvents(e, false);
19747                 }
19748             }
19749
19750             this.stopEvent(e);
19751
19752             return true;
19753         },
19754
19755         /**
19756          * Iterates over all of the DragDrop elements to find ones we are
19757          * hovering over or dropping on
19758          * @method fireEvents
19759          * @param {Event} e the event
19760          * @param {boolean} isDrop is this a drop op or a mouseover op?
19761          * @private
19762          * @static
19763          */
19764         fireEvents: function(e, isDrop) {
19765             var dc = this.dragCurrent;
19766
19767             // If the user did the mouse up outside of the window, we could
19768             // get here even though we have ended the drag.
19769             if (!dc || dc.isLocked()) {
19770                 return;
19771             }
19772
19773             var pt = e.getPoint();
19774
19775             // cache the previous dragOver array
19776             var oldOvers = [];
19777
19778             var outEvts   = [];
19779             var overEvts  = [];
19780             var dropEvts  = [];
19781             var enterEvts = [];
19782
19783             // Check to see if the object(s) we were hovering over is no longer
19784             // being hovered over so we can fire the onDragOut event
19785             for (var i in this.dragOvers) {
19786
19787                 var ddo = this.dragOvers[i];
19788
19789                 if (! this.isTypeOfDD(ddo)) {
19790                     continue;
19791                 }
19792
19793                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19794                     outEvts.push( ddo );
19795                 }
19796
19797                 oldOvers[i] = true;
19798                 delete this.dragOvers[i];
19799             }
19800
19801             for (var sGroup in dc.groups) {
19802
19803                 if ("string" != typeof sGroup) {
19804                     continue;
19805                 }
19806
19807                 for (i in this.ids[sGroup]) {
19808                     var oDD = this.ids[sGroup][i];
19809                     if (! this.isTypeOfDD(oDD)) {
19810                         continue;
19811                     }
19812
19813                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19814                         if (this.isOverTarget(pt, oDD, this.mode)) {
19815                             // look for drop interactions
19816                             if (isDrop) {
19817                                 dropEvts.push( oDD );
19818                             // look for drag enter and drag over interactions
19819                             } else {
19820
19821                                 // initial drag over: dragEnter fires
19822                                 if (!oldOvers[oDD.id]) {
19823                                     enterEvts.push( oDD );
19824                                 // subsequent drag overs: dragOver fires
19825                                 } else {
19826                                     overEvts.push( oDD );
19827                                 }
19828
19829                                 this.dragOvers[oDD.id] = oDD;
19830                             }
19831                         }
19832                     }
19833                 }
19834             }
19835
19836             if (this.mode) {
19837                 if (outEvts.length) {
19838                     dc.b4DragOut(e, outEvts);
19839                     dc.onDragOut(e, outEvts);
19840                 }
19841
19842                 if (enterEvts.length) {
19843                     dc.onDragEnter(e, enterEvts);
19844                 }
19845
19846                 if (overEvts.length) {
19847                     dc.b4DragOver(e, overEvts);
19848                     dc.onDragOver(e, overEvts);
19849                 }
19850
19851                 if (dropEvts.length) {
19852                     dc.b4DragDrop(e, dropEvts);
19853                     dc.onDragDrop(e, dropEvts);
19854                 }
19855
19856             } else {
19857                 // fire dragout events
19858                 var len = 0;
19859                 for (i=0, len=outEvts.length; i<len; ++i) {
19860                     dc.b4DragOut(e, outEvts[i].id);
19861                     dc.onDragOut(e, outEvts[i].id);
19862                 }
19863
19864                 // fire enter events
19865                 for (i=0,len=enterEvts.length; i<len; ++i) {
19866                     // dc.b4DragEnter(e, oDD.id);
19867                     dc.onDragEnter(e, enterEvts[i].id);
19868                 }
19869
19870                 // fire over events
19871                 for (i=0,len=overEvts.length; i<len; ++i) {
19872                     dc.b4DragOver(e, overEvts[i].id);
19873                     dc.onDragOver(e, overEvts[i].id);
19874                 }
19875
19876                 // fire drop events
19877                 for (i=0, len=dropEvts.length; i<len; ++i) {
19878                     dc.b4DragDrop(e, dropEvts[i].id);
19879                     dc.onDragDrop(e, dropEvts[i].id);
19880                 }
19881
19882             }
19883
19884             // notify about a drop that did not find a target
19885             if (isDrop && !dropEvts.length) {
19886                 dc.onInvalidDrop(e);
19887             }
19888
19889         },
19890
19891         /**
19892          * Helper function for getting the best match from the list of drag
19893          * and drop objects returned by the drag and drop events when we are
19894          * in INTERSECT mode.  It returns either the first object that the
19895          * cursor is over, or the object that has the greatest overlap with
19896          * the dragged element.
19897          * @method getBestMatch
19898          * @param  {DragDrop[]} dds The array of drag and drop objects
19899          * targeted
19900          * @return {DragDrop}       The best single match
19901          * @static
19902          */
19903         getBestMatch: function(dds) {
19904             var winner = null;
19905             // Return null if the input is not what we expect
19906             //if (!dds || !dds.length || dds.length == 0) {
19907                // winner = null;
19908             // If there is only one item, it wins
19909             //} else if (dds.length == 1) {
19910
19911             var len = dds.length;
19912
19913             if (len == 1) {
19914                 winner = dds[0];
19915             } else {
19916                 // Loop through the targeted items
19917                 for (var i=0; i<len; ++i) {
19918                     var dd = dds[i];
19919                     // If the cursor is over the object, it wins.  If the
19920                     // cursor is over multiple matches, the first one we come
19921                     // to wins.
19922                     if (dd.cursorIsOver) {
19923                         winner = dd;
19924                         break;
19925                     // Otherwise the object with the most overlap wins
19926                     } else {
19927                         if (!winner ||
19928                             winner.overlap.getArea() < dd.overlap.getArea()) {
19929                             winner = dd;
19930                         }
19931                     }
19932                 }
19933             }
19934
19935             return winner;
19936         },
19937
19938         /**
19939          * Refreshes the cache of the top-left and bottom-right points of the
19940          * drag and drop objects in the specified group(s).  This is in the
19941          * format that is stored in the drag and drop instance, so typical
19942          * usage is:
19943          * <code>
19944          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19945          * </code>
19946          * Alternatively:
19947          * <code>
19948          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19949          * </code>
19950          * @TODO this really should be an indexed array.  Alternatively this
19951          * method could accept both.
19952          * @method refreshCache
19953          * @param {Object} groups an associative array of groups to refresh
19954          * @static
19955          */
19956         refreshCache: function(groups) {
19957             for (var sGroup in groups) {
19958                 if ("string" != typeof sGroup) {
19959                     continue;
19960                 }
19961                 for (var i in this.ids[sGroup]) {
19962                     var oDD = this.ids[sGroup][i];
19963
19964                     if (this.isTypeOfDD(oDD)) {
19965                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
19966                         var loc = this.getLocation(oDD);
19967                         if (loc) {
19968                             this.locationCache[oDD.id] = loc;
19969                         } else {
19970                             delete this.locationCache[oDD.id];
19971                             // this will unregister the drag and drop object if
19972                             // the element is not in a usable state
19973                             // oDD.unreg();
19974                         }
19975                     }
19976                 }
19977             }
19978         },
19979
19980         /**
19981          * This checks to make sure an element exists and is in the DOM.  The
19982          * main purpose is to handle cases where innerHTML is used to remove
19983          * drag and drop objects from the DOM.  IE provides an 'unspecified
19984          * error' when trying to access the offsetParent of such an element
19985          * @method verifyEl
19986          * @param {HTMLElement} el the element to check
19987          * @return {boolean} true if the element looks usable
19988          * @static
19989          */
19990         verifyEl: function(el) {
19991             if (el) {
19992                 var parent;
19993                 if(Roo.isIE){
19994                     try{
19995                         parent = el.offsetParent;
19996                     }catch(e){}
19997                 }else{
19998                     parent = el.offsetParent;
19999                 }
20000                 if (parent) {
20001                     return true;
20002                 }
20003             }
20004
20005             return false;
20006         },
20007
20008         /**
20009          * Returns a Region object containing the drag and drop element's position
20010          * and size, including the padding configured for it
20011          * @method getLocation
20012          * @param {DragDrop} oDD the drag and drop object to get the
20013          *                       location for
20014          * @return {Roo.lib.Region} a Region object representing the total area
20015          *                             the element occupies, including any padding
20016          *                             the instance is configured for.
20017          * @static
20018          */
20019         getLocation: function(oDD) {
20020             if (! this.isTypeOfDD(oDD)) {
20021                 return null;
20022             }
20023
20024             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20025
20026             try {
20027                 pos= Roo.lib.Dom.getXY(el);
20028             } catch (e) { }
20029
20030             if (!pos) {
20031                 return null;
20032             }
20033
20034             x1 = pos[0];
20035             x2 = x1 + el.offsetWidth;
20036             y1 = pos[1];
20037             y2 = y1 + el.offsetHeight;
20038
20039             t = y1 - oDD.padding[0];
20040             r = x2 + oDD.padding[1];
20041             b = y2 + oDD.padding[2];
20042             l = x1 - oDD.padding[3];
20043
20044             return new Roo.lib.Region( t, r, b, l );
20045         },
20046
20047         /**
20048          * Checks the cursor location to see if it over the target
20049          * @method isOverTarget
20050          * @param {Roo.lib.Point} pt The point to evaluate
20051          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20052          * @return {boolean} true if the mouse is over the target
20053          * @private
20054          * @static
20055          */
20056         isOverTarget: function(pt, oTarget, intersect) {
20057             // use cache if available
20058             var loc = this.locationCache[oTarget.id];
20059             if (!loc || !this.useCache) {
20060                 loc = this.getLocation(oTarget);
20061                 this.locationCache[oTarget.id] = loc;
20062
20063             }
20064
20065             if (!loc) {
20066                 return false;
20067             }
20068
20069             oTarget.cursorIsOver = loc.contains( pt );
20070
20071             // DragDrop is using this as a sanity check for the initial mousedown
20072             // in this case we are done.  In POINT mode, if the drag obj has no
20073             // contraints, we are also done. Otherwise we need to evaluate the
20074             // location of the target as related to the actual location of the
20075             // dragged element.
20076             var dc = this.dragCurrent;
20077             if (!dc || !dc.getTargetCoord ||
20078                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20079                 return oTarget.cursorIsOver;
20080             }
20081
20082             oTarget.overlap = null;
20083
20084             // Get the current location of the drag element, this is the
20085             // location of the mouse event less the delta that represents
20086             // where the original mousedown happened on the element.  We
20087             // need to consider constraints and ticks as well.
20088             var pos = dc.getTargetCoord(pt.x, pt.y);
20089
20090             var el = dc.getDragEl();
20091             var curRegion = new Roo.lib.Region( pos.y,
20092                                                    pos.x + el.offsetWidth,
20093                                                    pos.y + el.offsetHeight,
20094                                                    pos.x );
20095
20096             var overlap = curRegion.intersect(loc);
20097
20098             if (overlap) {
20099                 oTarget.overlap = overlap;
20100                 return (intersect) ? true : oTarget.cursorIsOver;
20101             } else {
20102                 return false;
20103             }
20104         },
20105
20106         /**
20107          * unload event handler
20108          * @method _onUnload
20109          * @private
20110          * @static
20111          */
20112         _onUnload: function(e, me) {
20113             Roo.dd.DragDropMgr.unregAll();
20114         },
20115
20116         /**
20117          * Cleans up the drag and drop events and objects.
20118          * @method unregAll
20119          * @private
20120          * @static
20121          */
20122         unregAll: function() {
20123
20124             if (this.dragCurrent) {
20125                 this.stopDrag();
20126                 this.dragCurrent = null;
20127             }
20128
20129             this._execOnAll("unreg", []);
20130
20131             for (i in this.elementCache) {
20132                 delete this.elementCache[i];
20133             }
20134
20135             this.elementCache = {};
20136             this.ids = {};
20137         },
20138
20139         /**
20140          * A cache of DOM elements
20141          * @property elementCache
20142          * @private
20143          * @static
20144          */
20145         elementCache: {},
20146
20147         /**
20148          * Get the wrapper for the DOM element specified
20149          * @method getElWrapper
20150          * @param {String} id the id of the element to get
20151          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20152          * @private
20153          * @deprecated This wrapper isn't that useful
20154          * @static
20155          */
20156         getElWrapper: function(id) {
20157             var oWrapper = this.elementCache[id];
20158             if (!oWrapper || !oWrapper.el) {
20159                 oWrapper = this.elementCache[id] =
20160                     new this.ElementWrapper(Roo.getDom(id));
20161             }
20162             return oWrapper;
20163         },
20164
20165         /**
20166          * Returns the actual DOM element
20167          * @method getElement
20168          * @param {String} id the id of the elment to get
20169          * @return {Object} The element
20170          * @deprecated use Roo.getDom instead
20171          * @static
20172          */
20173         getElement: function(id) {
20174             return Roo.getDom(id);
20175         },
20176
20177         /**
20178          * Returns the style property for the DOM element (i.e.,
20179          * document.getElById(id).style)
20180          * @method getCss
20181          * @param {String} id the id of the elment to get
20182          * @return {Object} The style property of the element
20183          * @deprecated use Roo.getDom instead
20184          * @static
20185          */
20186         getCss: function(id) {
20187             var el = Roo.getDom(id);
20188             return (el) ? el.style : null;
20189         },
20190
20191         /**
20192          * Inner class for cached elements
20193          * @class DragDropMgr.ElementWrapper
20194          * @for DragDropMgr
20195          * @private
20196          * @deprecated
20197          */
20198         ElementWrapper: function(el) {
20199                 /**
20200                  * The element
20201                  * @property el
20202                  */
20203                 this.el = el || null;
20204                 /**
20205                  * The element id
20206                  * @property id
20207                  */
20208                 this.id = this.el && el.id;
20209                 /**
20210                  * A reference to the style property
20211                  * @property css
20212                  */
20213                 this.css = this.el && el.style;
20214             },
20215
20216         /**
20217          * Returns the X position of an html element
20218          * @method getPosX
20219          * @param el the element for which to get the position
20220          * @return {int} the X coordinate
20221          * @for DragDropMgr
20222          * @deprecated use Roo.lib.Dom.getX instead
20223          * @static
20224          */
20225         getPosX: function(el) {
20226             return Roo.lib.Dom.getX(el);
20227         },
20228
20229         /**
20230          * Returns the Y position of an html element
20231          * @method getPosY
20232          * @param el the element for which to get the position
20233          * @return {int} the Y coordinate
20234          * @deprecated use Roo.lib.Dom.getY instead
20235          * @static
20236          */
20237         getPosY: function(el) {
20238             return Roo.lib.Dom.getY(el);
20239         },
20240
20241         /**
20242          * Swap two nodes.  In IE, we use the native method, for others we
20243          * emulate the IE behavior
20244          * @method swapNode
20245          * @param n1 the first node to swap
20246          * @param n2 the other node to swap
20247          * @static
20248          */
20249         swapNode: function(n1, n2) {
20250             if (n1.swapNode) {
20251                 n1.swapNode(n2);
20252             } else {
20253                 var p = n2.parentNode;
20254                 var s = n2.nextSibling;
20255
20256                 if (s == n1) {
20257                     p.insertBefore(n1, n2);
20258                 } else if (n2 == n1.nextSibling) {
20259                     p.insertBefore(n2, n1);
20260                 } else {
20261                     n1.parentNode.replaceChild(n2, n1);
20262                     p.insertBefore(n1, s);
20263                 }
20264             }
20265         },
20266
20267         /**
20268          * Returns the current scroll position
20269          * @method getScroll
20270          * @private
20271          * @static
20272          */
20273         getScroll: function () {
20274             var t, l, dde=document.documentElement, db=document.body;
20275             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20276                 t = dde.scrollTop;
20277                 l = dde.scrollLeft;
20278             } else if (db) {
20279                 t = db.scrollTop;
20280                 l = db.scrollLeft;
20281             } else {
20282
20283             }
20284             return { top: t, left: l };
20285         },
20286
20287         /**
20288          * Returns the specified element style property
20289          * @method getStyle
20290          * @param {HTMLElement} el          the element
20291          * @param {string}      styleProp   the style property
20292          * @return {string} The value of the style property
20293          * @deprecated use Roo.lib.Dom.getStyle
20294          * @static
20295          */
20296         getStyle: function(el, styleProp) {
20297             return Roo.fly(el).getStyle(styleProp);
20298         },
20299
20300         /**
20301          * Gets the scrollTop
20302          * @method getScrollTop
20303          * @return {int} the document's scrollTop
20304          * @static
20305          */
20306         getScrollTop: function () { return this.getScroll().top; },
20307
20308         /**
20309          * Gets the scrollLeft
20310          * @method getScrollLeft
20311          * @return {int} the document's scrollTop
20312          * @static
20313          */
20314         getScrollLeft: function () { return this.getScroll().left; },
20315
20316         /**
20317          * Sets the x/y position of an element to the location of the
20318          * target element.
20319          * @method moveToEl
20320          * @param {HTMLElement} moveEl      The element to move
20321          * @param {HTMLElement} targetEl    The position reference element
20322          * @static
20323          */
20324         moveToEl: function (moveEl, targetEl) {
20325             var aCoord = Roo.lib.Dom.getXY(targetEl);
20326             Roo.lib.Dom.setXY(moveEl, aCoord);
20327         },
20328
20329         /**
20330          * Numeric array sort function
20331          * @method numericSort
20332          * @static
20333          */
20334         numericSort: function(a, b) { return (a - b); },
20335
20336         /**
20337          * Internal counter
20338          * @property _timeoutCount
20339          * @private
20340          * @static
20341          */
20342         _timeoutCount: 0,
20343
20344         /**
20345          * Trying to make the load order less important.  Without this we get
20346          * an error if this file is loaded before the Event Utility.
20347          * @method _addListeners
20348          * @private
20349          * @static
20350          */
20351         _addListeners: function() {
20352             var DDM = Roo.dd.DDM;
20353             if ( Roo.lib.Event && document ) {
20354                 DDM._onLoad();
20355             } else {
20356                 if (DDM._timeoutCount > 2000) {
20357                 } else {
20358                     setTimeout(DDM._addListeners, 10);
20359                     if (document && document.body) {
20360                         DDM._timeoutCount += 1;
20361                     }
20362                 }
20363             }
20364         },
20365
20366         /**
20367          * Recursively searches the immediate parent and all child nodes for
20368          * the handle element in order to determine wheter or not it was
20369          * clicked.
20370          * @method handleWasClicked
20371          * @param node the html element to inspect
20372          * @static
20373          */
20374         handleWasClicked: function(node, id) {
20375             if (this.isHandle(id, node.id)) {
20376                 return true;
20377             } else {
20378                 // check to see if this is a text node child of the one we want
20379                 var p = node.parentNode;
20380
20381                 while (p) {
20382                     if (this.isHandle(id, p.id)) {
20383                         return true;
20384                     } else {
20385                         p = p.parentNode;
20386                     }
20387                 }
20388             }
20389
20390             return false;
20391         }
20392
20393     };
20394
20395 }();
20396
20397 // shorter alias, save a few bytes
20398 Roo.dd.DDM = Roo.dd.DragDropMgr;
20399 Roo.dd.DDM._addListeners();
20400
20401 }/*
20402  * Based on:
20403  * Ext JS Library 1.1.1
20404  * Copyright(c) 2006-2007, Ext JS, LLC.
20405  *
20406  * Originally Released Under LGPL - original licence link has changed is not relivant.
20407  *
20408  * Fork - LGPL
20409  * <script type="text/javascript">
20410  */
20411
20412 /**
20413  * @class Roo.dd.DD
20414  * A DragDrop implementation where the linked element follows the
20415  * mouse cursor during a drag.
20416  * @extends Roo.dd.DragDrop
20417  * @constructor
20418  * @param {String} id the id of the linked element
20419  * @param {String} sGroup the group of related DragDrop items
20420  * @param {object} config an object containing configurable attributes
20421  *                Valid properties for DD:
20422  *                    scroll
20423  */
20424 Roo.dd.DD = function(id, sGroup, config) {
20425     if (id) {
20426         this.init(id, sGroup, config);
20427     }
20428 };
20429
20430 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20431
20432     /**
20433      * When set to true, the utility automatically tries to scroll the browser
20434      * window wehn a drag and drop element is dragged near the viewport boundary.
20435      * Defaults to true.
20436      * @property scroll
20437      * @type boolean
20438      */
20439     scroll: true,
20440
20441     /**
20442      * Sets the pointer offset to the distance between the linked element's top
20443      * left corner and the location the element was clicked
20444      * @method autoOffset
20445      * @param {int} iPageX the X coordinate of the click
20446      * @param {int} iPageY the Y coordinate of the click
20447      */
20448     autoOffset: function(iPageX, iPageY) {
20449         var x = iPageX - this.startPageX;
20450         var y = iPageY - this.startPageY;
20451         this.setDelta(x, y);
20452     },
20453
20454     /**
20455      * Sets the pointer offset.  You can call this directly to force the
20456      * offset to be in a particular location (e.g., pass in 0,0 to set it
20457      * to the center of the object)
20458      * @method setDelta
20459      * @param {int} iDeltaX the distance from the left
20460      * @param {int} iDeltaY the distance from the top
20461      */
20462     setDelta: function(iDeltaX, iDeltaY) {
20463         this.deltaX = iDeltaX;
20464         this.deltaY = iDeltaY;
20465     },
20466
20467     /**
20468      * Sets the drag element to the location of the mousedown or click event,
20469      * maintaining the cursor location relative to the location on the element
20470      * that was clicked.  Override this if you want to place the element in a
20471      * location other than where the cursor is.
20472      * @method setDragElPos
20473      * @param {int} iPageX the X coordinate of the mousedown or drag event
20474      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20475      */
20476     setDragElPos: function(iPageX, iPageY) {
20477         // the first time we do this, we are going to check to make sure
20478         // the element has css positioning
20479
20480         var el = this.getDragEl();
20481         this.alignElWithMouse(el, iPageX, iPageY);
20482     },
20483
20484     /**
20485      * Sets the element to the location of the mousedown or click event,
20486      * maintaining the cursor location relative to the location on the element
20487      * that was clicked.  Override this if you want to place the element in a
20488      * location other than where the cursor is.
20489      * @method alignElWithMouse
20490      * @param {HTMLElement} el the element to move
20491      * @param {int} iPageX the X coordinate of the mousedown or drag event
20492      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20493      */
20494     alignElWithMouse: function(el, iPageX, iPageY) {
20495         var oCoord = this.getTargetCoord(iPageX, iPageY);
20496         var fly = el.dom ? el : Roo.fly(el);
20497         if (!this.deltaSetXY) {
20498             var aCoord = [oCoord.x, oCoord.y];
20499             fly.setXY(aCoord);
20500             var newLeft = fly.getLeft(true);
20501             var newTop  = fly.getTop(true);
20502             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20503         } else {
20504             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20505         }
20506
20507         this.cachePosition(oCoord.x, oCoord.y);
20508         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20509         return oCoord;
20510     },
20511
20512     /**
20513      * Saves the most recent position so that we can reset the constraints and
20514      * tick marks on-demand.  We need to know this so that we can calculate the
20515      * number of pixels the element is offset from its original position.
20516      * @method cachePosition
20517      * @param iPageX the current x position (optional, this just makes it so we
20518      * don't have to look it up again)
20519      * @param iPageY the current y position (optional, this just makes it so we
20520      * don't have to look it up again)
20521      */
20522     cachePosition: function(iPageX, iPageY) {
20523         if (iPageX) {
20524             this.lastPageX = iPageX;
20525             this.lastPageY = iPageY;
20526         } else {
20527             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20528             this.lastPageX = aCoord[0];
20529             this.lastPageY = aCoord[1];
20530         }
20531     },
20532
20533     /**
20534      * Auto-scroll the window if the dragged object has been moved beyond the
20535      * visible window boundary.
20536      * @method autoScroll
20537      * @param {int} x the drag element's x position
20538      * @param {int} y the drag element's y position
20539      * @param {int} h the height of the drag element
20540      * @param {int} w the width of the drag element
20541      * @private
20542      */
20543     autoScroll: function(x, y, h, w) {
20544
20545         if (this.scroll) {
20546             // The client height
20547             var clientH = Roo.lib.Dom.getViewWidth();
20548
20549             // The client width
20550             var clientW = Roo.lib.Dom.getViewHeight();
20551
20552             // The amt scrolled down
20553             var st = this.DDM.getScrollTop();
20554
20555             // The amt scrolled right
20556             var sl = this.DDM.getScrollLeft();
20557
20558             // Location of the bottom of the element
20559             var bot = h + y;
20560
20561             // Location of the right of the element
20562             var right = w + x;
20563
20564             // The distance from the cursor to the bottom of the visible area,
20565             // adjusted so that we don't scroll if the cursor is beyond the
20566             // element drag constraints
20567             var toBot = (clientH + st - y - this.deltaY);
20568
20569             // The distance from the cursor to the right of the visible area
20570             var toRight = (clientW + sl - x - this.deltaX);
20571
20572
20573             // How close to the edge the cursor must be before we scroll
20574             // var thresh = (document.all) ? 100 : 40;
20575             var thresh = 40;
20576
20577             // How many pixels to scroll per autoscroll op.  This helps to reduce
20578             // clunky scrolling. IE is more sensitive about this ... it needs this
20579             // value to be higher.
20580             var scrAmt = (document.all) ? 80 : 30;
20581
20582             // Scroll down if we are near the bottom of the visible page and the
20583             // obj extends below the crease
20584             if ( bot > clientH && toBot < thresh ) {
20585                 window.scrollTo(sl, st + scrAmt);
20586             }
20587
20588             // Scroll up if the window is scrolled down and the top of the object
20589             // goes above the top border
20590             if ( y < st && st > 0 && y - st < thresh ) {
20591                 window.scrollTo(sl, st - scrAmt);
20592             }
20593
20594             // Scroll right if the obj is beyond the right border and the cursor is
20595             // near the border.
20596             if ( right > clientW && toRight < thresh ) {
20597                 window.scrollTo(sl + scrAmt, st);
20598             }
20599
20600             // Scroll left if the window has been scrolled to the right and the obj
20601             // extends past the left border
20602             if ( x < sl && sl > 0 && x - sl < thresh ) {
20603                 window.scrollTo(sl - scrAmt, st);
20604             }
20605         }
20606     },
20607
20608     /**
20609      * Finds the location the element should be placed if we want to move
20610      * it to where the mouse location less the click offset would place us.
20611      * @method getTargetCoord
20612      * @param {int} iPageX the X coordinate of the click
20613      * @param {int} iPageY the Y coordinate of the click
20614      * @return an object that contains the coordinates (Object.x and Object.y)
20615      * @private
20616      */
20617     getTargetCoord: function(iPageX, iPageY) {
20618
20619
20620         var x = iPageX - this.deltaX;
20621         var y = iPageY - this.deltaY;
20622
20623         if (this.constrainX) {
20624             if (x < this.minX) { x = this.minX; }
20625             if (x > this.maxX) { x = this.maxX; }
20626         }
20627
20628         if (this.constrainY) {
20629             if (y < this.minY) { y = this.minY; }
20630             if (y > this.maxY) { y = this.maxY; }
20631         }
20632
20633         x = this.getTick(x, this.xTicks);
20634         y = this.getTick(y, this.yTicks);
20635
20636
20637         return {x:x, y:y};
20638     },
20639
20640     /*
20641      * Sets up config options specific to this class. Overrides
20642      * Roo.dd.DragDrop, but all versions of this method through the
20643      * inheritance chain are called
20644      */
20645     applyConfig: function() {
20646         Roo.dd.DD.superclass.applyConfig.call(this);
20647         this.scroll = (this.config.scroll !== false);
20648     },
20649
20650     /*
20651      * Event that fires prior to the onMouseDown event.  Overrides
20652      * Roo.dd.DragDrop.
20653      */
20654     b4MouseDown: function(e) {
20655         // this.resetConstraints();
20656         this.autoOffset(e.getPageX(),
20657                             e.getPageY());
20658     },
20659
20660     /*
20661      * Event that fires prior to the onDrag event.  Overrides
20662      * Roo.dd.DragDrop.
20663      */
20664     b4Drag: function(e) {
20665         this.setDragElPos(e.getPageX(),
20666                             e.getPageY());
20667     },
20668
20669     toString: function() {
20670         return ("DD " + this.id);
20671     }
20672
20673     //////////////////////////////////////////////////////////////////////////
20674     // Debugging ygDragDrop events that can be overridden
20675     //////////////////////////////////////////////////////////////////////////
20676     /*
20677     startDrag: function(x, y) {
20678     },
20679
20680     onDrag: function(e) {
20681     },
20682
20683     onDragEnter: function(e, id) {
20684     },
20685
20686     onDragOver: function(e, id) {
20687     },
20688
20689     onDragOut: function(e, id) {
20690     },
20691
20692     onDragDrop: function(e, id) {
20693     },
20694
20695     endDrag: function(e) {
20696     }
20697
20698     */
20699
20700 });/*
20701  * Based on:
20702  * Ext JS Library 1.1.1
20703  * Copyright(c) 2006-2007, Ext JS, LLC.
20704  *
20705  * Originally Released Under LGPL - original licence link has changed is not relivant.
20706  *
20707  * Fork - LGPL
20708  * <script type="text/javascript">
20709  */
20710
20711 /**
20712  * @class Roo.dd.DDProxy
20713  * A DragDrop implementation that inserts an empty, bordered div into
20714  * the document that follows the cursor during drag operations.  At the time of
20715  * the click, the frame div is resized to the dimensions of the linked html
20716  * element, and moved to the exact location of the linked element.
20717  *
20718  * References to the "frame" element refer to the single proxy element that
20719  * was created to be dragged in place of all DDProxy elements on the
20720  * page.
20721  *
20722  * @extends Roo.dd.DD
20723  * @constructor
20724  * @param {String} id the id of the linked html element
20725  * @param {String} sGroup the group of related DragDrop objects
20726  * @param {object} config an object containing configurable attributes
20727  *                Valid properties for DDProxy in addition to those in DragDrop:
20728  *                   resizeFrame, centerFrame, dragElId
20729  */
20730 Roo.dd.DDProxy = function(id, sGroup, config) {
20731     if (id) {
20732         this.init(id, sGroup, config);
20733         this.initFrame();
20734     }
20735 };
20736
20737 /**
20738  * The default drag frame div id
20739  * @property Roo.dd.DDProxy.dragElId
20740  * @type String
20741  * @static
20742  */
20743 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20744
20745 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20746
20747     /**
20748      * By default we resize the drag frame to be the same size as the element
20749      * we want to drag (this is to get the frame effect).  We can turn it off
20750      * if we want a different behavior.
20751      * @property resizeFrame
20752      * @type boolean
20753      */
20754     resizeFrame: true,
20755
20756     /**
20757      * By default the frame is positioned exactly where the drag element is, so
20758      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20759      * you do not have constraints on the obj is to have the drag frame centered
20760      * around the cursor.  Set centerFrame to true for this effect.
20761      * @property centerFrame
20762      * @type boolean
20763      */
20764     centerFrame: false,
20765
20766     /**
20767      * Creates the proxy element if it does not yet exist
20768      * @method createFrame
20769      */
20770     createFrame: function() {
20771         var self = this;
20772         var body = document.body;
20773
20774         if (!body || !body.firstChild) {
20775             setTimeout( function() { self.createFrame(); }, 50 );
20776             return;
20777         }
20778
20779         var div = this.getDragEl();
20780
20781         if (!div) {
20782             div    = document.createElement("div");
20783             div.id = this.dragElId;
20784             var s  = div.style;
20785
20786             s.position   = "absolute";
20787             s.visibility = "hidden";
20788             s.cursor     = "move";
20789             s.border     = "2px solid #aaa";
20790             s.zIndex     = 999;
20791
20792             // appendChild can blow up IE if invoked prior to the window load event
20793             // while rendering a table.  It is possible there are other scenarios
20794             // that would cause this to happen as well.
20795             body.insertBefore(div, body.firstChild);
20796         }
20797     },
20798
20799     /**
20800      * Initialization for the drag frame element.  Must be called in the
20801      * constructor of all subclasses
20802      * @method initFrame
20803      */
20804     initFrame: function() {
20805         this.createFrame();
20806     },
20807
20808     applyConfig: function() {
20809         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20810
20811         this.resizeFrame = (this.config.resizeFrame !== false);
20812         this.centerFrame = (this.config.centerFrame);
20813         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20814     },
20815
20816     /**
20817      * Resizes the drag frame to the dimensions of the clicked object, positions
20818      * it over the object, and finally displays it
20819      * @method showFrame
20820      * @param {int} iPageX X click position
20821      * @param {int} iPageY Y click position
20822      * @private
20823      */
20824     showFrame: function(iPageX, iPageY) {
20825         var el = this.getEl();
20826         var dragEl = this.getDragEl();
20827         var s = dragEl.style;
20828
20829         this._resizeProxy();
20830
20831         if (this.centerFrame) {
20832             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20833                            Math.round(parseInt(s.height, 10)/2) );
20834         }
20835
20836         this.setDragElPos(iPageX, iPageY);
20837
20838         Roo.fly(dragEl).show();
20839     },
20840
20841     /**
20842      * The proxy is automatically resized to the dimensions of the linked
20843      * element when a drag is initiated, unless resizeFrame is set to false
20844      * @method _resizeProxy
20845      * @private
20846      */
20847     _resizeProxy: function() {
20848         if (this.resizeFrame) {
20849             var el = this.getEl();
20850             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20851         }
20852     },
20853
20854     // overrides Roo.dd.DragDrop
20855     b4MouseDown: function(e) {
20856         var x = e.getPageX();
20857         var y = e.getPageY();
20858         this.autoOffset(x, y);
20859         this.setDragElPos(x, y);
20860     },
20861
20862     // overrides Roo.dd.DragDrop
20863     b4StartDrag: function(x, y) {
20864         // show the drag frame
20865         this.showFrame(x, y);
20866     },
20867
20868     // overrides Roo.dd.DragDrop
20869     b4EndDrag: function(e) {
20870         Roo.fly(this.getDragEl()).hide();
20871     },
20872
20873     // overrides Roo.dd.DragDrop
20874     // By default we try to move the element to the last location of the frame.
20875     // This is so that the default behavior mirrors that of Roo.dd.DD.
20876     endDrag: function(e) {
20877
20878         var lel = this.getEl();
20879         var del = this.getDragEl();
20880
20881         // Show the drag frame briefly so we can get its position
20882         del.style.visibility = "";
20883
20884         this.beforeMove();
20885         // Hide the linked element before the move to get around a Safari
20886         // rendering bug.
20887         lel.style.visibility = "hidden";
20888         Roo.dd.DDM.moveToEl(lel, del);
20889         del.style.visibility = "hidden";
20890         lel.style.visibility = "";
20891
20892         this.afterDrag();
20893     },
20894
20895     beforeMove : function(){
20896
20897     },
20898
20899     afterDrag : function(){
20900
20901     },
20902
20903     toString: function() {
20904         return ("DDProxy " + this.id);
20905     }
20906
20907 });
20908 /*
20909  * Based on:
20910  * Ext JS Library 1.1.1
20911  * Copyright(c) 2006-2007, Ext JS, LLC.
20912  *
20913  * Originally Released Under LGPL - original licence link has changed is not relivant.
20914  *
20915  * Fork - LGPL
20916  * <script type="text/javascript">
20917  */
20918
20919  /**
20920  * @class Roo.dd.DDTarget
20921  * A DragDrop implementation that does not move, but can be a drop
20922  * target.  You would get the same result by simply omitting implementation
20923  * for the event callbacks, but this way we reduce the processing cost of the
20924  * event listener and the callbacks.
20925  * @extends Roo.dd.DragDrop
20926  * @constructor
20927  * @param {String} id the id of the element that is a drop target
20928  * @param {String} sGroup the group of related DragDrop objects
20929  * @param {object} config an object containing configurable attributes
20930  *                 Valid properties for DDTarget in addition to those in
20931  *                 DragDrop:
20932  *                    none
20933  */
20934 Roo.dd.DDTarget = function(id, sGroup, config) {
20935     if (id) {
20936         this.initTarget(id, sGroup, config);
20937     }
20938     if (config.listeners || config.events) { 
20939        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20940             listeners : config.listeners || {}, 
20941             events : config.events || {} 
20942         });    
20943     }
20944 };
20945
20946 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20947 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20948     toString: function() {
20949         return ("DDTarget " + this.id);
20950     }
20951 });
20952 /*
20953  * Based on:
20954  * Ext JS Library 1.1.1
20955  * Copyright(c) 2006-2007, Ext JS, LLC.
20956  *
20957  * Originally Released Under LGPL - original licence link has changed is not relivant.
20958  *
20959  * Fork - LGPL
20960  * <script type="text/javascript">
20961  */
20962  
20963
20964 /**
20965  * @class Roo.dd.ScrollManager
20966  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
20967  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
20968  * @singleton
20969  */
20970 Roo.dd.ScrollManager = function(){
20971     var ddm = Roo.dd.DragDropMgr;
20972     var els = {};
20973     var dragEl = null;
20974     var proc = {};
20975     
20976     
20977     
20978     var onStop = function(e){
20979         dragEl = null;
20980         clearProc();
20981     };
20982     
20983     var triggerRefresh = function(){
20984         if(ddm.dragCurrent){
20985              ddm.refreshCache(ddm.dragCurrent.groups);
20986         }
20987     };
20988     
20989     var doScroll = function(){
20990         if(ddm.dragCurrent){
20991             var dds = Roo.dd.ScrollManager;
20992             if(!dds.animate){
20993                 if(proc.el.scroll(proc.dir, dds.increment)){
20994                     triggerRefresh();
20995                 }
20996             }else{
20997                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
20998             }
20999         }
21000     };
21001     
21002     var clearProc = function(){
21003         if(proc.id){
21004             clearInterval(proc.id);
21005         }
21006         proc.id = 0;
21007         proc.el = null;
21008         proc.dir = "";
21009     };
21010     
21011     var startProc = function(el, dir){
21012          Roo.log('scroll startproc');
21013         clearProc();
21014         proc.el = el;
21015         proc.dir = dir;
21016         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21017     };
21018     
21019     var onFire = function(e, isDrop){
21020        
21021         if(isDrop || !ddm.dragCurrent){ return; }
21022         var dds = Roo.dd.ScrollManager;
21023         if(!dragEl || dragEl != ddm.dragCurrent){
21024             dragEl = ddm.dragCurrent;
21025             // refresh regions on drag start
21026             dds.refreshCache();
21027         }
21028         
21029         var xy = Roo.lib.Event.getXY(e);
21030         var pt = new Roo.lib.Point(xy[0], xy[1]);
21031         for(var id in els){
21032             var el = els[id], r = el._region;
21033             if(r && r.contains(pt) && el.isScrollable()){
21034                 if(r.bottom - pt.y <= dds.thresh){
21035                     if(proc.el != el){
21036                         startProc(el, "down");
21037                     }
21038                     return;
21039                 }else if(r.right - pt.x <= dds.thresh){
21040                     if(proc.el != el){
21041                         startProc(el, "left");
21042                     }
21043                     return;
21044                 }else if(pt.y - r.top <= dds.thresh){
21045                     if(proc.el != el){
21046                         startProc(el, "up");
21047                     }
21048                     return;
21049                 }else if(pt.x - r.left <= dds.thresh){
21050                     if(proc.el != el){
21051                         startProc(el, "right");
21052                     }
21053                     return;
21054                 }
21055             }
21056         }
21057         clearProc();
21058     };
21059     
21060     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21061     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21062     
21063     return {
21064         /**
21065          * Registers new overflow element(s) to auto scroll
21066          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21067          */
21068         register : function(el){
21069             if(el instanceof Array){
21070                 for(var i = 0, len = el.length; i < len; i++) {
21071                         this.register(el[i]);
21072                 }
21073             }else{
21074                 el = Roo.get(el);
21075                 els[el.id] = el;
21076             }
21077             Roo.dd.ScrollManager.els = els;
21078         },
21079         
21080         /**
21081          * Unregisters overflow element(s) so they are no longer scrolled
21082          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21083          */
21084         unregister : function(el){
21085             if(el instanceof Array){
21086                 for(var i = 0, len = el.length; i < len; i++) {
21087                         this.unregister(el[i]);
21088                 }
21089             }else{
21090                 el = Roo.get(el);
21091                 delete els[el.id];
21092             }
21093         },
21094         
21095         /**
21096          * The number of pixels from the edge of a container the pointer needs to be to 
21097          * trigger scrolling (defaults to 25)
21098          * @type Number
21099          */
21100         thresh : 25,
21101         
21102         /**
21103          * The number of pixels to scroll in each scroll increment (defaults to 50)
21104          * @type Number
21105          */
21106         increment : 100,
21107         
21108         /**
21109          * The frequency of scrolls in milliseconds (defaults to 500)
21110          * @type Number
21111          */
21112         frequency : 500,
21113         
21114         /**
21115          * True to animate the scroll (defaults to true)
21116          * @type Boolean
21117          */
21118         animate: true,
21119         
21120         /**
21121          * The animation duration in seconds - 
21122          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21123          * @type Number
21124          */
21125         animDuration: .4,
21126         
21127         /**
21128          * Manually trigger a cache refresh.
21129          */
21130         refreshCache : function(){
21131             for(var id in els){
21132                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21133                     els[id]._region = els[id].getRegion();
21134                 }
21135             }
21136         }
21137     };
21138 }();/*
21139  * Based on:
21140  * Ext JS Library 1.1.1
21141  * Copyright(c) 2006-2007, Ext JS, LLC.
21142  *
21143  * Originally Released Under LGPL - original licence link has changed is not relivant.
21144  *
21145  * Fork - LGPL
21146  * <script type="text/javascript">
21147  */
21148  
21149
21150 /**
21151  * @class Roo.dd.Registry
21152  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21153  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21154  * @singleton
21155  */
21156 Roo.dd.Registry = function(){
21157     var elements = {}; 
21158     var handles = {}; 
21159     var autoIdSeed = 0;
21160
21161     var getId = function(el, autogen){
21162         if(typeof el == "string"){
21163             return el;
21164         }
21165         var id = el.id;
21166         if(!id && autogen !== false){
21167             id = "roodd-" + (++autoIdSeed);
21168             el.id = id;
21169         }
21170         return id;
21171     };
21172     
21173     return {
21174     /**
21175      * Register a drag drop element
21176      * @param {String|HTMLElement} element The id or DOM node to register
21177      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21178      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21179      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21180      * populated in the data object (if applicable):
21181      * <pre>
21182 Value      Description<br />
21183 ---------  ------------------------------------------<br />
21184 handles    Array of DOM nodes that trigger dragging<br />
21185            for the element being registered<br />
21186 isHandle   True if the element passed in triggers<br />
21187            dragging itself, else false
21188 </pre>
21189      */
21190         register : function(el, data){
21191             data = data || {};
21192             if(typeof el == "string"){
21193                 el = document.getElementById(el);
21194             }
21195             data.ddel = el;
21196             elements[getId(el)] = data;
21197             if(data.isHandle !== false){
21198                 handles[data.ddel.id] = data;
21199             }
21200             if(data.handles){
21201                 var hs = data.handles;
21202                 for(var i = 0, len = hs.length; i < len; i++){
21203                         handles[getId(hs[i])] = data;
21204                 }
21205             }
21206         },
21207
21208     /**
21209      * Unregister a drag drop element
21210      * @param {String|HTMLElement}  element The id or DOM node to unregister
21211      */
21212         unregister : function(el){
21213             var id = getId(el, false);
21214             var data = elements[id];
21215             if(data){
21216                 delete elements[id];
21217                 if(data.handles){
21218                     var hs = data.handles;
21219                     for(var i = 0, len = hs.length; i < len; i++){
21220                         delete handles[getId(hs[i], false)];
21221                     }
21222                 }
21223             }
21224         },
21225
21226     /**
21227      * Returns the handle registered for a DOM Node by id
21228      * @param {String|HTMLElement} id The DOM node or id to look up
21229      * @return {Object} handle The custom handle data
21230      */
21231         getHandle : function(id){
21232             if(typeof id != "string"){ // must be element?
21233                 id = id.id;
21234             }
21235             return handles[id];
21236         },
21237
21238     /**
21239      * Returns the handle that is registered for the DOM node that is the target of the event
21240      * @param {Event} e The event
21241      * @return {Object} handle The custom handle data
21242      */
21243         getHandleFromEvent : function(e){
21244             var t = Roo.lib.Event.getTarget(e);
21245             return t ? handles[t.id] : null;
21246         },
21247
21248     /**
21249      * Returns a custom data object that is registered for a DOM node by id
21250      * @param {String|HTMLElement} id The DOM node or id to look up
21251      * @return {Object} data The custom data
21252      */
21253         getTarget : function(id){
21254             if(typeof id != "string"){ // must be element?
21255                 id = id.id;
21256             }
21257             return elements[id];
21258         },
21259
21260     /**
21261      * Returns a custom data object that is registered for the DOM node that is the target of the event
21262      * @param {Event} e The event
21263      * @return {Object} data The custom data
21264      */
21265         getTargetFromEvent : function(e){
21266             var t = Roo.lib.Event.getTarget(e);
21267             return t ? elements[t.id] || handles[t.id] : null;
21268         }
21269     };
21270 }();/*
21271  * Based on:
21272  * Ext JS Library 1.1.1
21273  * Copyright(c) 2006-2007, Ext JS, LLC.
21274  *
21275  * Originally Released Under LGPL - original licence link has changed is not relivant.
21276  *
21277  * Fork - LGPL
21278  * <script type="text/javascript">
21279  */
21280  
21281
21282 /**
21283  * @class Roo.dd.StatusProxy
21284  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21285  * default drag proxy used by all Roo.dd components.
21286  * @constructor
21287  * @param {Object} config
21288  */
21289 Roo.dd.StatusProxy = function(config){
21290     Roo.apply(this, config);
21291     this.id = this.id || Roo.id();
21292     this.el = new Roo.Layer({
21293         dh: {
21294             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21295                 {tag: "div", cls: "x-dd-drop-icon"},
21296                 {tag: "div", cls: "x-dd-drag-ghost"}
21297             ]
21298         }, 
21299         shadow: !config || config.shadow !== false
21300     });
21301     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21302     this.dropStatus = this.dropNotAllowed;
21303 };
21304
21305 Roo.dd.StatusProxy.prototype = {
21306     /**
21307      * @cfg {String} dropAllowed
21308      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21309      */
21310     dropAllowed : "x-dd-drop-ok",
21311     /**
21312      * @cfg {String} dropNotAllowed
21313      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21314      */
21315     dropNotAllowed : "x-dd-drop-nodrop",
21316
21317     /**
21318      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21319      * over the current target element.
21320      * @param {String} cssClass The css class for the new drop status indicator image
21321      */
21322     setStatus : function(cssClass){
21323         cssClass = cssClass || this.dropNotAllowed;
21324         if(this.dropStatus != cssClass){
21325             this.el.replaceClass(this.dropStatus, cssClass);
21326             this.dropStatus = cssClass;
21327         }
21328     },
21329
21330     /**
21331      * Resets the status indicator to the default dropNotAllowed value
21332      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21333      */
21334     reset : function(clearGhost){
21335         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21336         this.dropStatus = this.dropNotAllowed;
21337         if(clearGhost){
21338             this.ghost.update("");
21339         }
21340     },
21341
21342     /**
21343      * Updates the contents of the ghost element
21344      * @param {String} html The html that will replace the current innerHTML of the ghost element
21345      */
21346     update : function(html){
21347         if(typeof html == "string"){
21348             this.ghost.update(html);
21349         }else{
21350             this.ghost.update("");
21351             html.style.margin = "0";
21352             this.ghost.dom.appendChild(html);
21353         }
21354         // ensure float = none set?? cant remember why though.
21355         var el = this.ghost.dom.firstChild;
21356                 if(el){
21357                         Roo.fly(el).setStyle('float', 'none');
21358                 }
21359     },
21360     
21361     /**
21362      * Returns the underlying proxy {@link Roo.Layer}
21363      * @return {Roo.Layer} el
21364     */
21365     getEl : function(){
21366         return this.el;
21367     },
21368
21369     /**
21370      * Returns the ghost element
21371      * @return {Roo.Element} el
21372      */
21373     getGhost : function(){
21374         return this.ghost;
21375     },
21376
21377     /**
21378      * Hides the proxy
21379      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21380      */
21381     hide : function(clear){
21382         this.el.hide();
21383         if(clear){
21384             this.reset(true);
21385         }
21386     },
21387
21388     /**
21389      * Stops the repair animation if it's currently running
21390      */
21391     stop : function(){
21392         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21393             this.anim.stop();
21394         }
21395     },
21396
21397     /**
21398      * Displays this proxy
21399      */
21400     show : function(){
21401         this.el.show();
21402     },
21403
21404     /**
21405      * Force the Layer to sync its shadow and shim positions to the element
21406      */
21407     sync : function(){
21408         this.el.sync();
21409     },
21410
21411     /**
21412      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21413      * invalid drop operation by the item being dragged.
21414      * @param {Array} xy The XY position of the element ([x, y])
21415      * @param {Function} callback The function to call after the repair is complete
21416      * @param {Object} scope The scope in which to execute the callback
21417      */
21418     repair : function(xy, callback, scope){
21419         this.callback = callback;
21420         this.scope = scope;
21421         if(xy && this.animRepair !== false){
21422             this.el.addClass("x-dd-drag-repair");
21423             this.el.hideUnders(true);
21424             this.anim = this.el.shift({
21425                 duration: this.repairDuration || .5,
21426                 easing: 'easeOut',
21427                 xy: xy,
21428                 stopFx: true,
21429                 callback: this.afterRepair,
21430                 scope: this
21431             });
21432         }else{
21433             this.afterRepair();
21434         }
21435     },
21436
21437     // private
21438     afterRepair : function(){
21439         this.hide(true);
21440         if(typeof this.callback == "function"){
21441             this.callback.call(this.scope || this);
21442         }
21443         this.callback = null;
21444         this.scope = null;
21445     }
21446 };/*
21447  * Based on:
21448  * Ext JS Library 1.1.1
21449  * Copyright(c) 2006-2007, Ext JS, LLC.
21450  *
21451  * Originally Released Under LGPL - original licence link has changed is not relivant.
21452  *
21453  * Fork - LGPL
21454  * <script type="text/javascript">
21455  */
21456
21457 /**
21458  * @class Roo.dd.DragSource
21459  * @extends Roo.dd.DDProxy
21460  * A simple class that provides the basic implementation needed to make any element draggable.
21461  * @constructor
21462  * @param {String/HTMLElement/Element} el The container element
21463  * @param {Object} config
21464  */
21465 Roo.dd.DragSource = function(el, config){
21466     this.el = Roo.get(el);
21467     this.dragData = {};
21468     
21469     Roo.apply(this, config);
21470     
21471     if(!this.proxy){
21472         this.proxy = new Roo.dd.StatusProxy();
21473     }
21474
21475     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21476           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21477     
21478     this.dragging = false;
21479 };
21480
21481 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21482     /**
21483      * @cfg {String} dropAllowed
21484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21485      */
21486     dropAllowed : "x-dd-drop-ok",
21487     /**
21488      * @cfg {String} dropNotAllowed
21489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21490      */
21491     dropNotAllowed : "x-dd-drop-nodrop",
21492
21493     /**
21494      * Returns the data object associated with this drag source
21495      * @return {Object} data An object containing arbitrary data
21496      */
21497     getDragData : function(e){
21498         return this.dragData;
21499     },
21500
21501     // private
21502     onDragEnter : function(e, id){
21503         var target = Roo.dd.DragDropMgr.getDDById(id);
21504         this.cachedTarget = target;
21505         if(this.beforeDragEnter(target, e, id) !== false){
21506             if(target.isNotifyTarget){
21507                 var status = target.notifyEnter(this, e, this.dragData);
21508                 this.proxy.setStatus(status);
21509             }else{
21510                 this.proxy.setStatus(this.dropAllowed);
21511             }
21512             
21513             if(this.afterDragEnter){
21514                 /**
21515                  * An empty function by default, but provided so that you can perform a custom action
21516                  * when the dragged item enters the drop target by providing an implementation.
21517                  * @param {Roo.dd.DragDrop} target The drop target
21518                  * @param {Event} e The event object
21519                  * @param {String} id The id of the dragged element
21520                  * @method afterDragEnter
21521                  */
21522                 this.afterDragEnter(target, e, id);
21523             }
21524         }
21525     },
21526
21527     /**
21528      * An empty function by default, but provided so that you can perform a custom action
21529      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21530      * @param {Roo.dd.DragDrop} target The drop target
21531      * @param {Event} e The event object
21532      * @param {String} id The id of the dragged element
21533      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21534      */
21535     beforeDragEnter : function(target, e, id){
21536         return true;
21537     },
21538
21539     // private
21540     alignElWithMouse: function() {
21541         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21542         this.proxy.sync();
21543     },
21544
21545     // private
21546     onDragOver : function(e, id){
21547         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21548         if(this.beforeDragOver(target, e, id) !== false){
21549             if(target.isNotifyTarget){
21550                 var status = target.notifyOver(this, e, this.dragData);
21551                 this.proxy.setStatus(status);
21552             }
21553
21554             if(this.afterDragOver){
21555                 /**
21556                  * An empty function by default, but provided so that you can perform a custom action
21557                  * while the dragged item is over the drop target by providing an implementation.
21558                  * @param {Roo.dd.DragDrop} target The drop target
21559                  * @param {Event} e The event object
21560                  * @param {String} id The id of the dragged element
21561                  * @method afterDragOver
21562                  */
21563                 this.afterDragOver(target, e, id);
21564             }
21565         }
21566     },
21567
21568     /**
21569      * An empty function by default, but provided so that you can perform a custom action
21570      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21571      * @param {Roo.dd.DragDrop} target The drop target
21572      * @param {Event} e The event object
21573      * @param {String} id The id of the dragged element
21574      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21575      */
21576     beforeDragOver : function(target, e, id){
21577         return true;
21578     },
21579
21580     // private
21581     onDragOut : function(e, id){
21582         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21583         if(this.beforeDragOut(target, e, id) !== false){
21584             if(target.isNotifyTarget){
21585                 target.notifyOut(this, e, this.dragData);
21586             }
21587             this.proxy.reset();
21588             if(this.afterDragOut){
21589                 /**
21590                  * An empty function by default, but provided so that you can perform a custom action
21591                  * after the dragged item is dragged out of the target without dropping.
21592                  * @param {Roo.dd.DragDrop} target The drop target
21593                  * @param {Event} e The event object
21594                  * @param {String} id The id of the dragged element
21595                  * @method afterDragOut
21596                  */
21597                 this.afterDragOut(target, e, id);
21598             }
21599         }
21600         this.cachedTarget = null;
21601     },
21602
21603     /**
21604      * An empty function by default, but provided so that you can perform a custom action before the dragged
21605      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21606      * @param {Roo.dd.DragDrop} target The drop target
21607      * @param {Event} e The event object
21608      * @param {String} id The id of the dragged element
21609      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21610      */
21611     beforeDragOut : function(target, e, id){
21612         return true;
21613     },
21614     
21615     // private
21616     onDragDrop : function(e, id){
21617         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21618         if(this.beforeDragDrop(target, e, id) !== false){
21619             if(target.isNotifyTarget){
21620                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21621                     this.onValidDrop(target, e, id);
21622                 }else{
21623                     this.onInvalidDrop(target, e, id);
21624                 }
21625             }else{
21626                 this.onValidDrop(target, e, id);
21627             }
21628             
21629             if(this.afterDragDrop){
21630                 /**
21631                  * An empty function by default, but provided so that you can perform a custom action
21632                  * after a valid drag drop has occurred by providing an implementation.
21633                  * @param {Roo.dd.DragDrop} target The drop target
21634                  * @param {Event} e The event object
21635                  * @param {String} id The id of the dropped element
21636                  * @method afterDragDrop
21637                  */
21638                 this.afterDragDrop(target, e, id);
21639             }
21640         }
21641         delete this.cachedTarget;
21642     },
21643
21644     /**
21645      * An empty function by default, but provided so that you can perform a custom action before the dragged
21646      * item is dropped onto the target and optionally cancel the onDragDrop.
21647      * @param {Roo.dd.DragDrop} target The drop target
21648      * @param {Event} e The event object
21649      * @param {String} id The id of the dragged element
21650      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21651      */
21652     beforeDragDrop : function(target, e, id){
21653         return true;
21654     },
21655
21656     // private
21657     onValidDrop : function(target, e, id){
21658         this.hideProxy();
21659         if(this.afterValidDrop){
21660             /**
21661              * An empty function by default, but provided so that you can perform a custom action
21662              * after a valid drop has occurred by providing an implementation.
21663              * @param {Object} target The target DD 
21664              * @param {Event} e The event object
21665              * @param {String} id The id of the dropped element
21666              * @method afterInvalidDrop
21667              */
21668             this.afterValidDrop(target, e, id);
21669         }
21670     },
21671
21672     // private
21673     getRepairXY : function(e, data){
21674         return this.el.getXY();  
21675     },
21676
21677     // private
21678     onInvalidDrop : function(target, e, id){
21679         this.beforeInvalidDrop(target, e, id);
21680         if(this.cachedTarget){
21681             if(this.cachedTarget.isNotifyTarget){
21682                 this.cachedTarget.notifyOut(this, e, this.dragData);
21683             }
21684             this.cacheTarget = null;
21685         }
21686         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21687
21688         if(this.afterInvalidDrop){
21689             /**
21690              * An empty function by default, but provided so that you can perform a custom action
21691              * after an invalid drop has occurred by providing an implementation.
21692              * @param {Event} e The event object
21693              * @param {String} id The id of the dropped element
21694              * @method afterInvalidDrop
21695              */
21696             this.afterInvalidDrop(e, id);
21697         }
21698     },
21699
21700     // private
21701     afterRepair : function(){
21702         if(Roo.enableFx){
21703             this.el.highlight(this.hlColor || "c3daf9");
21704         }
21705         this.dragging = false;
21706     },
21707
21708     /**
21709      * An empty function by default, but provided so that you can perform a custom action after an invalid
21710      * drop has occurred.
21711      * @param {Roo.dd.DragDrop} target The drop target
21712      * @param {Event} e The event object
21713      * @param {String} id The id of the dragged element
21714      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21715      */
21716     beforeInvalidDrop : function(target, e, id){
21717         return true;
21718     },
21719
21720     // private
21721     handleMouseDown : function(e){
21722         if(this.dragging) {
21723             return;
21724         }
21725         var data = this.getDragData(e);
21726         if(data && this.onBeforeDrag(data, e) !== false){
21727             this.dragData = data;
21728             this.proxy.stop();
21729             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21730         } 
21731     },
21732
21733     /**
21734      * An empty function by default, but provided so that you can perform a custom action before the initial
21735      * drag event begins and optionally cancel it.
21736      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21737      * @param {Event} e The event object
21738      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21739      */
21740     onBeforeDrag : function(data, e){
21741         return true;
21742     },
21743
21744     /**
21745      * An empty function by default, but provided so that you can perform a custom action once the initial
21746      * drag event has begun.  The drag cannot be canceled from this function.
21747      * @param {Number} x The x position of the click on the dragged object
21748      * @param {Number} y The y position of the click on the dragged object
21749      */
21750     onStartDrag : Roo.emptyFn,
21751
21752     // private - YUI override
21753     startDrag : function(x, y){
21754         this.proxy.reset();
21755         this.dragging = true;
21756         this.proxy.update("");
21757         this.onInitDrag(x, y);
21758         this.proxy.show();
21759     },
21760
21761     // private
21762     onInitDrag : function(x, y){
21763         var clone = this.el.dom.cloneNode(true);
21764         clone.id = Roo.id(); // prevent duplicate ids
21765         this.proxy.update(clone);
21766         this.onStartDrag(x, y);
21767         return true;
21768     },
21769
21770     /**
21771      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21772      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21773      */
21774     getProxy : function(){
21775         return this.proxy;  
21776     },
21777
21778     /**
21779      * Hides the drag source's {@link Roo.dd.StatusProxy}
21780      */
21781     hideProxy : function(){
21782         this.proxy.hide();  
21783         this.proxy.reset(true);
21784         this.dragging = false;
21785     },
21786
21787     // private
21788     triggerCacheRefresh : function(){
21789         Roo.dd.DDM.refreshCache(this.groups);
21790     },
21791
21792     // private - override to prevent hiding
21793     b4EndDrag: function(e) {
21794     },
21795
21796     // private - override to prevent moving
21797     endDrag : function(e){
21798         this.onEndDrag(this.dragData, e);
21799     },
21800
21801     // private
21802     onEndDrag : function(data, e){
21803     },
21804     
21805     // private - pin to cursor
21806     autoOffset : function(x, y) {
21807         this.setDelta(-12, -20);
21808     }    
21809 });/*
21810  * Based on:
21811  * Ext JS Library 1.1.1
21812  * Copyright(c) 2006-2007, Ext JS, LLC.
21813  *
21814  * Originally Released Under LGPL - original licence link has changed is not relivant.
21815  *
21816  * Fork - LGPL
21817  * <script type="text/javascript">
21818  */
21819
21820
21821 /**
21822  * @class Roo.dd.DropTarget
21823  * @extends Roo.dd.DDTarget
21824  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21825  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21826  * @constructor
21827  * @param {String/HTMLElement/Element} el The container element
21828  * @param {Object} config
21829  */
21830 Roo.dd.DropTarget = function(el, config){
21831     this.el = Roo.get(el);
21832     
21833     var listeners = false; ;
21834     if (config && config.listeners) {
21835         listeners= config.listeners;
21836         delete config.listeners;
21837     }
21838     Roo.apply(this, config);
21839     
21840     if(this.containerScroll){
21841         Roo.dd.ScrollManager.register(this.el);
21842     }
21843     this.addEvents( {
21844          /**
21845          * @scope Roo.dd.DropTarget
21846          */
21847          
21848          /**
21849          * @event enter
21850          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21851          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21852          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21853          * 
21854          * IMPORTANT : it should set this.overClass and this.dropAllowed
21855          * 
21856          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21857          * @param {Event} e The event
21858          * @param {Object} data An object containing arbitrary data supplied by the drag source
21859          */
21860         "enter" : true,
21861         
21862          /**
21863          * @event over
21864          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21865          * This method will be called on every mouse movement while the drag source is over the drop target.
21866          * This default implementation simply returns the dropAllowed config value.
21867          * 
21868          * IMPORTANT : it should set this.dropAllowed
21869          * 
21870          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21871          * @param {Event} e The event
21872          * @param {Object} data An object containing arbitrary data supplied by the drag source
21873          
21874          */
21875         "over" : true,
21876         /**
21877          * @event out
21878          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21879          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21880          * overClass (if any) from the drop element.
21881          * 
21882          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21883          * @param {Event} e The event
21884          * @param {Object} data An object containing arbitrary data supplied by the drag source
21885          */
21886          "out" : true,
21887          
21888         /**
21889          * @event drop
21890          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21891          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21892          * implementation that does something to process the drop event and returns true so that the drag source's
21893          * repair action does not run.
21894          * 
21895          * IMPORTANT : it should set this.success
21896          * 
21897          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21898          * @param {Event} e The event
21899          * @param {Object} data An object containing arbitrary data supplied by the drag source
21900         */
21901          "drop" : true
21902     });
21903             
21904      
21905     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21906         this.el.dom, 
21907         this.ddGroup || this.group,
21908         {
21909             isTarget: true,
21910             listeners : listeners || {} 
21911            
21912         
21913         }
21914     );
21915
21916 };
21917
21918 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21919     /**
21920      * @cfg {String} overClass
21921      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21922      */
21923      /**
21924      * @cfg {String} ddGroup
21925      * The drag drop group to handle drop events for
21926      */
21927      
21928     /**
21929      * @cfg {String} dropAllowed
21930      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21931      */
21932     dropAllowed : "x-dd-drop-ok",
21933     /**
21934      * @cfg {String} dropNotAllowed
21935      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21936      */
21937     dropNotAllowed : "x-dd-drop-nodrop",
21938     /**
21939      * @cfg {boolean} success
21940      * set this after drop listener.. 
21941      */
21942     success : false,
21943     /**
21944      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21945      * if the drop point is valid for over/enter..
21946      */
21947     valid : false,
21948     // private
21949     isTarget : true,
21950
21951     // private
21952     isNotifyTarget : true,
21953     
21954     /**
21955      * @hide
21956      */
21957     notifyEnter : function(dd, e, data)
21958     {
21959         this.valid = true;
21960         this.fireEvent('enter', dd, e, data);
21961         if(this.overClass){
21962             this.el.addClass(this.overClass);
21963         }
21964         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21965             this.valid ? this.dropAllowed : this.dropNotAllowed
21966         );
21967     },
21968
21969     /**
21970      * @hide
21971      */
21972     notifyOver : function(dd, e, data)
21973     {
21974         this.valid = true;
21975         this.fireEvent('over', dd, e, data);
21976         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21977             this.valid ? this.dropAllowed : this.dropNotAllowed
21978         );
21979     },
21980
21981     /**
21982      * @hide
21983      */
21984     notifyOut : function(dd, e, data)
21985     {
21986         this.fireEvent('out', dd, e, data);
21987         if(this.overClass){
21988             this.el.removeClass(this.overClass);
21989         }
21990     },
21991
21992     /**
21993      * @hide
21994      */
21995     notifyDrop : function(dd, e, data)
21996     {
21997         this.success = false;
21998         this.fireEvent('drop', dd, e, data);
21999         return this.success;
22000     }
22001 });/*
22002  * Based on:
22003  * Ext JS Library 1.1.1
22004  * Copyright(c) 2006-2007, Ext JS, LLC.
22005  *
22006  * Originally Released Under LGPL - original licence link has changed is not relivant.
22007  *
22008  * Fork - LGPL
22009  * <script type="text/javascript">
22010  */
22011
22012
22013 /**
22014  * @class Roo.dd.DragZone
22015  * @extends Roo.dd.DragSource
22016  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22017  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22018  * @constructor
22019  * @param {String/HTMLElement/Element} el The container element
22020  * @param {Object} config
22021  */
22022 Roo.dd.DragZone = function(el, config){
22023     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22024     if(this.containerScroll){
22025         Roo.dd.ScrollManager.register(this.el);
22026     }
22027 };
22028
22029 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22030     /**
22031      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22032      * for auto scrolling during drag operations.
22033      */
22034     /**
22035      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22036      * method after a failed drop (defaults to "c3daf9" - light blue)
22037      */
22038
22039     /**
22040      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22041      * for a valid target to drag based on the mouse down. Override this method
22042      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22043      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22044      * @param {EventObject} e The mouse down event
22045      * @return {Object} The dragData
22046      */
22047     getDragData : function(e){
22048         return Roo.dd.Registry.getHandleFromEvent(e);
22049     },
22050     
22051     /**
22052      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22053      * this.dragData.ddel
22054      * @param {Number} x The x position of the click on the dragged object
22055      * @param {Number} y The y position of the click on the dragged object
22056      * @return {Boolean} true to continue the drag, false to cancel
22057      */
22058     onInitDrag : function(x, y){
22059         this.proxy.update(this.dragData.ddel.cloneNode(true));
22060         this.onStartDrag(x, y);
22061         return true;
22062     },
22063     
22064     /**
22065      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22066      */
22067     afterRepair : function(){
22068         if(Roo.enableFx){
22069             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22070         }
22071         this.dragging = false;
22072     },
22073
22074     /**
22075      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22076      * the XY of this.dragData.ddel
22077      * @param {EventObject} e The mouse up event
22078      * @return {Array} The xy location (e.g. [100, 200])
22079      */
22080     getRepairXY : function(e){
22081         return Roo.Element.fly(this.dragData.ddel).getXY();  
22082     }
22083 });/*
22084  * Based on:
22085  * Ext JS Library 1.1.1
22086  * Copyright(c) 2006-2007, Ext JS, LLC.
22087  *
22088  * Originally Released Under LGPL - original licence link has changed is not relivant.
22089  *
22090  * Fork - LGPL
22091  * <script type="text/javascript">
22092  */
22093 /**
22094  * @class Roo.dd.DropZone
22095  * @extends Roo.dd.DropTarget
22096  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22097  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22098  * @constructor
22099  * @param {String/HTMLElement/Element} el The container element
22100  * @param {Object} config
22101  */
22102 Roo.dd.DropZone = function(el, config){
22103     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22104 };
22105
22106 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22107     /**
22108      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22109      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22110      * provide your own custom lookup.
22111      * @param {Event} e The event
22112      * @return {Object} data The custom data
22113      */
22114     getTargetFromEvent : function(e){
22115         return Roo.dd.Registry.getTargetFromEvent(e);
22116     },
22117
22118     /**
22119      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22120      * that it has registered.  This method has no default implementation and should be overridden to provide
22121      * node-specific processing if necessary.
22122      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22123      * {@link #getTargetFromEvent} for this node)
22124      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22125      * @param {Event} e The event
22126      * @param {Object} data An object containing arbitrary data supplied by the drag source
22127      */
22128     onNodeEnter : function(n, dd, e, data){
22129         
22130     },
22131
22132     /**
22133      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22134      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22135      * overridden to provide the proper feedback.
22136      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22137      * {@link #getTargetFromEvent} for this node)
22138      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22139      * @param {Event} e The event
22140      * @param {Object} data An object containing arbitrary data supplied by the drag source
22141      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22142      * underlying {@link Roo.dd.StatusProxy} can be updated
22143      */
22144     onNodeOver : function(n, dd, e, data){
22145         return this.dropAllowed;
22146     },
22147
22148     /**
22149      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22150      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22151      * node-specific processing if necessary.
22152      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22153      * {@link #getTargetFromEvent} for this node)
22154      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22155      * @param {Event} e The event
22156      * @param {Object} data An object containing arbitrary data supplied by the drag source
22157      */
22158     onNodeOut : function(n, dd, e, data){
22159         
22160     },
22161
22162     /**
22163      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22164      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22165      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22166      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22167      * {@link #getTargetFromEvent} for this node)
22168      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22169      * @param {Event} e The event
22170      * @param {Object} data An object containing arbitrary data supplied by the drag source
22171      * @return {Boolean} True if the drop was valid, else false
22172      */
22173     onNodeDrop : function(n, dd, e, data){
22174         return false;
22175     },
22176
22177     /**
22178      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22179      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22180      * it should be overridden to provide the proper feedback if necessary.
22181      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22182      * @param {Event} e The event
22183      * @param {Object} data An object containing arbitrary data supplied by the drag source
22184      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22185      * underlying {@link Roo.dd.StatusProxy} can be updated
22186      */
22187     onContainerOver : function(dd, e, data){
22188         return this.dropNotAllowed;
22189     },
22190
22191     /**
22192      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22193      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22194      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22195      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22196      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22197      * @param {Event} e The event
22198      * @param {Object} data An object containing arbitrary data supplied by the drag source
22199      * @return {Boolean} True if the drop was valid, else false
22200      */
22201     onContainerDrop : function(dd, e, data){
22202         return false;
22203     },
22204
22205     /**
22206      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22207      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22208      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22209      * you should override this method and provide a custom implementation.
22210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22211      * @param {Event} e The event
22212      * @param {Object} data An object containing arbitrary data supplied by the drag source
22213      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22214      * underlying {@link Roo.dd.StatusProxy} can be updated
22215      */
22216     notifyEnter : function(dd, e, data){
22217         return this.dropNotAllowed;
22218     },
22219
22220     /**
22221      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22222      * This method will be called on every mouse movement while the drag source is over the drop zone.
22223      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22224      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22225      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22226      * registered node, it will call {@link #onContainerOver}.
22227      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22228      * @param {Event} e The event
22229      * @param {Object} data An object containing arbitrary data supplied by the drag source
22230      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22231      * underlying {@link Roo.dd.StatusProxy} can be updated
22232      */
22233     notifyOver : function(dd, e, data){
22234         var n = this.getTargetFromEvent(e);
22235         if(!n){ // not over valid drop target
22236             if(this.lastOverNode){
22237                 this.onNodeOut(this.lastOverNode, dd, e, data);
22238                 this.lastOverNode = null;
22239             }
22240             return this.onContainerOver(dd, e, data);
22241         }
22242         if(this.lastOverNode != n){
22243             if(this.lastOverNode){
22244                 this.onNodeOut(this.lastOverNode, dd, e, data);
22245             }
22246             this.onNodeEnter(n, dd, e, data);
22247             this.lastOverNode = n;
22248         }
22249         return this.onNodeOver(n, dd, e, data);
22250     },
22251
22252     /**
22253      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22254      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22255      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22256      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22257      * @param {Event} e The event
22258      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22259      */
22260     notifyOut : function(dd, e, data){
22261         if(this.lastOverNode){
22262             this.onNodeOut(this.lastOverNode, dd, e, data);
22263             this.lastOverNode = null;
22264         }
22265     },
22266
22267     /**
22268      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22269      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22270      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22271      * otherwise it will call {@link #onContainerDrop}.
22272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22273      * @param {Event} e The event
22274      * @param {Object} data An object containing arbitrary data supplied by the drag source
22275      * @return {Boolean} True if the drop was valid, else false
22276      */
22277     notifyDrop : function(dd, e, data){
22278         if(this.lastOverNode){
22279             this.onNodeOut(this.lastOverNode, dd, e, data);
22280             this.lastOverNode = null;
22281         }
22282         var n = this.getTargetFromEvent(e);
22283         return n ?
22284             this.onNodeDrop(n, dd, e, data) :
22285             this.onContainerDrop(dd, e, data);
22286     },
22287
22288     // private
22289     triggerCacheRefresh : function(){
22290         Roo.dd.DDM.refreshCache(this.groups);
22291     }  
22292 });/*
22293  * Based on:
22294  * Ext JS Library 1.1.1
22295  * Copyright(c) 2006-2007, Ext JS, LLC.
22296  *
22297  * Originally Released Under LGPL - original licence link has changed is not relivant.
22298  *
22299  * Fork - LGPL
22300  * <script type="text/javascript">
22301  */
22302
22303
22304 /**
22305  * @class Roo.data.SortTypes
22306  * @singleton
22307  * Defines the default sorting (casting?) comparison functions used when sorting data.
22308  */
22309 Roo.data.SortTypes = {
22310     /**
22311      * Default sort that does nothing
22312      * @param {Mixed} s The value being converted
22313      * @return {Mixed} The comparison value
22314      */
22315     none : function(s){
22316         return s;
22317     },
22318     
22319     /**
22320      * The regular expression used to strip tags
22321      * @type {RegExp}
22322      * @property
22323      */
22324     stripTagsRE : /<\/?[^>]+>/gi,
22325     
22326     /**
22327      * Strips all HTML tags to sort on text only
22328      * @param {Mixed} s The value being converted
22329      * @return {String} The comparison value
22330      */
22331     asText : function(s){
22332         return String(s).replace(this.stripTagsRE, "");
22333     },
22334     
22335     /**
22336      * Strips all HTML tags to sort on text only - Case insensitive
22337      * @param {Mixed} s The value being converted
22338      * @return {String} The comparison value
22339      */
22340     asUCText : function(s){
22341         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22342     },
22343     
22344     /**
22345      * Case insensitive string
22346      * @param {Mixed} s The value being converted
22347      * @return {String} The comparison value
22348      */
22349     asUCString : function(s) {
22350         return String(s).toUpperCase();
22351     },
22352     
22353     /**
22354      * Date sorting
22355      * @param {Mixed} s The value being converted
22356      * @return {Number} The comparison value
22357      */
22358     asDate : function(s) {
22359         if(!s){
22360             return 0;
22361         }
22362         if(s instanceof Date){
22363             return s.getTime();
22364         }
22365         return Date.parse(String(s));
22366     },
22367     
22368     /**
22369      * Float sorting
22370      * @param {Mixed} s The value being converted
22371      * @return {Float} The comparison value
22372      */
22373     asFloat : function(s) {
22374         var val = parseFloat(String(s).replace(/,/g, ""));
22375         if(isNaN(val)) {
22376             val = 0;
22377         }
22378         return val;
22379     },
22380     
22381     /**
22382      * Integer sorting
22383      * @param {Mixed} s The value being converted
22384      * @return {Number} The comparison value
22385      */
22386     asInt : function(s) {
22387         var val = parseInt(String(s).replace(/,/g, ""));
22388         if(isNaN(val)) {
22389             val = 0;
22390         }
22391         return val;
22392     }
22393 };/*
22394  * Based on:
22395  * Ext JS Library 1.1.1
22396  * Copyright(c) 2006-2007, Ext JS, LLC.
22397  *
22398  * Originally Released Under LGPL - original licence link has changed is not relivant.
22399  *
22400  * Fork - LGPL
22401  * <script type="text/javascript">
22402  */
22403
22404 /**
22405 * @class Roo.data.Record
22406  * Instances of this class encapsulate both record <em>definition</em> information, and record
22407  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22408  * to access Records cached in an {@link Roo.data.Store} object.<br>
22409  * <p>
22410  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22411  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22412  * objects.<br>
22413  * <p>
22414  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22415  * @constructor
22416  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22417  * {@link #create}. The parameters are the same.
22418  * @param {Array} data An associative Array of data values keyed by the field name.
22419  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22420  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22421  * not specified an integer id is generated.
22422  */
22423 Roo.data.Record = function(data, id){
22424     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22425     this.data = data;
22426 };
22427
22428 /**
22429  * Generate a constructor for a specific record layout.
22430  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22431  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22432  * Each field definition object may contain the following properties: <ul>
22433  * <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,
22434  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22435  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22436  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22437  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22438  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22439  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22440  * this may be omitted.</p></li>
22441  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22442  * <ul><li>auto (Default, implies no conversion)</li>
22443  * <li>string</li>
22444  * <li>int</li>
22445  * <li>float</li>
22446  * <li>boolean</li>
22447  * <li>date</li></ul></p></li>
22448  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22449  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22450  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22451  * by the Reader into an object that will be stored in the Record. It is passed the
22452  * following parameters:<ul>
22453  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22454  * </ul></p></li>
22455  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22456  * </ul>
22457  * <br>usage:<br><pre><code>
22458 var TopicRecord = Roo.data.Record.create(
22459     {name: 'title', mapping: 'topic_title'},
22460     {name: 'author', mapping: 'username'},
22461     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22462     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22463     {name: 'lastPoster', mapping: 'user2'},
22464     {name: 'excerpt', mapping: 'post_text'}
22465 );
22466
22467 var myNewRecord = new TopicRecord({
22468     title: 'Do my job please',
22469     author: 'noobie',
22470     totalPosts: 1,
22471     lastPost: new Date(),
22472     lastPoster: 'Animal',
22473     excerpt: 'No way dude!'
22474 });
22475 myStore.add(myNewRecord);
22476 </code></pre>
22477  * @method create
22478  * @static
22479  */
22480 Roo.data.Record.create = function(o){
22481     var f = function(){
22482         f.superclass.constructor.apply(this, arguments);
22483     };
22484     Roo.extend(f, Roo.data.Record);
22485     var p = f.prototype;
22486     p.fields = new Roo.util.MixedCollection(false, function(field){
22487         return field.name;
22488     });
22489     for(var i = 0, len = o.length; i < len; i++){
22490         p.fields.add(new Roo.data.Field(o[i]));
22491     }
22492     f.getField = function(name){
22493         return p.fields.get(name);  
22494     };
22495     return f;
22496 };
22497
22498 Roo.data.Record.AUTO_ID = 1000;
22499 Roo.data.Record.EDIT = 'edit';
22500 Roo.data.Record.REJECT = 'reject';
22501 Roo.data.Record.COMMIT = 'commit';
22502
22503 Roo.data.Record.prototype = {
22504     /**
22505      * Readonly flag - true if this record has been modified.
22506      * @type Boolean
22507      */
22508     dirty : false,
22509     editing : false,
22510     error: null,
22511     modified: null,
22512
22513     // private
22514     join : function(store){
22515         this.store = store;
22516     },
22517
22518     /**
22519      * Set the named field to the specified value.
22520      * @param {String} name The name of the field to set.
22521      * @param {Object} value The value to set the field to.
22522      */
22523     set : function(name, value){
22524         if(this.data[name] == value){
22525             return;
22526         }
22527         this.dirty = true;
22528         if(!this.modified){
22529             this.modified = {};
22530         }
22531         if(typeof this.modified[name] == 'undefined'){
22532             this.modified[name] = this.data[name];
22533         }
22534         this.data[name] = value;
22535         if(!this.editing && this.store){
22536             this.store.afterEdit(this);
22537         }       
22538     },
22539
22540     /**
22541      * Get the value of the named field.
22542      * @param {String} name The name of the field to get the value of.
22543      * @return {Object} The value of the field.
22544      */
22545     get : function(name){
22546         return this.data[name]; 
22547     },
22548
22549     // private
22550     beginEdit : function(){
22551         this.editing = true;
22552         this.modified = {}; 
22553     },
22554
22555     // private
22556     cancelEdit : function(){
22557         this.editing = false;
22558         delete this.modified;
22559     },
22560
22561     // private
22562     endEdit : function(){
22563         this.editing = false;
22564         if(this.dirty && this.store){
22565             this.store.afterEdit(this);
22566         }
22567     },
22568
22569     /**
22570      * Usually called by the {@link Roo.data.Store} which owns the Record.
22571      * Rejects all changes made to the Record since either creation, or the last commit operation.
22572      * Modified fields are reverted to their original values.
22573      * <p>
22574      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22575      * of reject operations.
22576      */
22577     reject : function(){
22578         var m = this.modified;
22579         for(var n in m){
22580             if(typeof m[n] != "function"){
22581                 this.data[n] = m[n];
22582             }
22583         }
22584         this.dirty = false;
22585         delete this.modified;
22586         this.editing = false;
22587         if(this.store){
22588             this.store.afterReject(this);
22589         }
22590     },
22591
22592     /**
22593      * Usually called by the {@link Roo.data.Store} which owns the Record.
22594      * Commits all changes made to the Record since either creation, or the last commit operation.
22595      * <p>
22596      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22597      * of commit operations.
22598      */
22599     commit : function(){
22600         this.dirty = false;
22601         delete this.modified;
22602         this.editing = false;
22603         if(this.store){
22604             this.store.afterCommit(this);
22605         }
22606     },
22607
22608     // private
22609     hasError : function(){
22610         return this.error != null;
22611     },
22612
22613     // private
22614     clearError : function(){
22615         this.error = null;
22616     },
22617
22618     /**
22619      * Creates a copy of this record.
22620      * @param {String} id (optional) A new record id if you don't want to use this record's id
22621      * @return {Record}
22622      */
22623     copy : function(newId) {
22624         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22625     }
22626 };/*
22627  * Based on:
22628  * Ext JS Library 1.1.1
22629  * Copyright(c) 2006-2007, Ext JS, LLC.
22630  *
22631  * Originally Released Under LGPL - original licence link has changed is not relivant.
22632  *
22633  * Fork - LGPL
22634  * <script type="text/javascript">
22635  */
22636
22637
22638
22639 /**
22640  * @class Roo.data.Store
22641  * @extends Roo.util.Observable
22642  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22643  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22644  * <p>
22645  * 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
22646  * has no knowledge of the format of the data returned by the Proxy.<br>
22647  * <p>
22648  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22649  * instances from the data object. These records are cached and made available through accessor functions.
22650  * @constructor
22651  * Creates a new Store.
22652  * @param {Object} config A config object containing the objects needed for the Store to access data,
22653  * and read the data into Records.
22654  */
22655 Roo.data.Store = function(config){
22656     this.data = new Roo.util.MixedCollection(false);
22657     this.data.getKey = function(o){
22658         return o.id;
22659     };
22660     this.baseParams = {};
22661     // private
22662     this.paramNames = {
22663         "start" : "start",
22664         "limit" : "limit",
22665         "sort" : "sort",
22666         "dir" : "dir",
22667         "multisort" : "_multisort"
22668     };
22669
22670     if(config && config.data){
22671         this.inlineData = config.data;
22672         delete config.data;
22673     }
22674
22675     Roo.apply(this, config);
22676     
22677     if(this.reader){ // reader passed
22678         this.reader = Roo.factory(this.reader, Roo.data);
22679         this.reader.xmodule = this.xmodule || false;
22680         if(!this.recordType){
22681             this.recordType = this.reader.recordType;
22682         }
22683         if(this.reader.onMetaChange){
22684             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22685         }
22686     }
22687
22688     if(this.recordType){
22689         this.fields = this.recordType.prototype.fields;
22690     }
22691     this.modified = [];
22692
22693     this.addEvents({
22694         /**
22695          * @event datachanged
22696          * Fires when the data cache has changed, and a widget which is using this Store
22697          * as a Record cache should refresh its view.
22698          * @param {Store} this
22699          */
22700         datachanged : true,
22701         /**
22702          * @event metachange
22703          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22704          * @param {Store} this
22705          * @param {Object} meta The JSON metadata
22706          */
22707         metachange : true,
22708         /**
22709          * @event add
22710          * Fires when Records have been added to the Store
22711          * @param {Store} this
22712          * @param {Roo.data.Record[]} records The array of Records added
22713          * @param {Number} index The index at which the record(s) were added
22714          */
22715         add : true,
22716         /**
22717          * @event remove
22718          * Fires when a Record has been removed from the Store
22719          * @param {Store} this
22720          * @param {Roo.data.Record} record The Record that was removed
22721          * @param {Number} index The index at which the record was removed
22722          */
22723         remove : true,
22724         /**
22725          * @event update
22726          * Fires when a Record has been updated
22727          * @param {Store} this
22728          * @param {Roo.data.Record} record The Record that was updated
22729          * @param {String} operation The update operation being performed.  Value may be one of:
22730          * <pre><code>
22731  Roo.data.Record.EDIT
22732  Roo.data.Record.REJECT
22733  Roo.data.Record.COMMIT
22734          * </code></pre>
22735          */
22736         update : true,
22737         /**
22738          * @event clear
22739          * Fires when the data cache has been cleared.
22740          * @param {Store} this
22741          */
22742         clear : true,
22743         /**
22744          * @event beforeload
22745          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22746          * the load action will be canceled.
22747          * @param {Store} this
22748          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22749          */
22750         beforeload : true,
22751         /**
22752          * @event beforeloadadd
22753          * Fires after a new set of Records has been loaded.
22754          * @param {Store} this
22755          * @param {Roo.data.Record[]} records The Records that were loaded
22756          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22757          */
22758         beforeloadadd : true,
22759         /**
22760          * @event load
22761          * Fires after a new set of Records has been loaded, before they are added to the store.
22762          * @param {Store} this
22763          * @param {Roo.data.Record[]} records The Records that were loaded
22764          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22765          * @params {Object} return from reader
22766          */
22767         load : true,
22768         /**
22769          * @event loadexception
22770          * Fires if an exception occurs in the Proxy during loading.
22771          * Called with the signature of the Proxy's "loadexception" event.
22772          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22773          * 
22774          * @param {Proxy} 
22775          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22776          * @param {Object} load options 
22777          * @param {Object} jsonData from your request (normally this contains the Exception)
22778          */
22779         loadexception : true
22780     });
22781     
22782     if(this.proxy){
22783         this.proxy = Roo.factory(this.proxy, Roo.data);
22784         this.proxy.xmodule = this.xmodule || false;
22785         this.relayEvents(this.proxy,  ["loadexception"]);
22786     }
22787     this.sortToggle = {};
22788     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22789
22790     Roo.data.Store.superclass.constructor.call(this);
22791
22792     if(this.inlineData){
22793         this.loadData(this.inlineData);
22794         delete this.inlineData;
22795     }
22796 };
22797
22798 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22799      /**
22800     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22801     * without a remote query - used by combo/forms at present.
22802     */
22803     
22804     /**
22805     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22806     */
22807     /**
22808     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22809     */
22810     /**
22811     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22812     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22813     */
22814     /**
22815     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22816     * on any HTTP request
22817     */
22818     /**
22819     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22820     */
22821     /**
22822     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22823     */
22824     multiSort: false,
22825     /**
22826     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22827     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22828     */
22829     remoteSort : false,
22830
22831     /**
22832     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22833      * loaded or when a record is removed. (defaults to false).
22834     */
22835     pruneModifiedRecords : false,
22836
22837     // private
22838     lastOptions : null,
22839
22840     /**
22841      * Add Records to the Store and fires the add event.
22842      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22843      */
22844     add : function(records){
22845         records = [].concat(records);
22846         for(var i = 0, len = records.length; i < len; i++){
22847             records[i].join(this);
22848         }
22849         var index = this.data.length;
22850         this.data.addAll(records);
22851         this.fireEvent("add", this, records, index);
22852     },
22853
22854     /**
22855      * Remove a Record from the Store and fires the remove event.
22856      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22857      */
22858     remove : function(record){
22859         var index = this.data.indexOf(record);
22860         this.data.removeAt(index);
22861         if(this.pruneModifiedRecords){
22862             this.modified.remove(record);
22863         }
22864         this.fireEvent("remove", this, record, index);
22865     },
22866
22867     /**
22868      * Remove all Records from the Store and fires the clear event.
22869      */
22870     removeAll : function(){
22871         this.data.clear();
22872         if(this.pruneModifiedRecords){
22873             this.modified = [];
22874         }
22875         this.fireEvent("clear", this);
22876     },
22877
22878     /**
22879      * Inserts Records to the Store at the given index and fires the add event.
22880      * @param {Number} index The start index at which to insert the passed Records.
22881      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22882      */
22883     insert : function(index, records){
22884         records = [].concat(records);
22885         for(var i = 0, len = records.length; i < len; i++){
22886             this.data.insert(index, records[i]);
22887             records[i].join(this);
22888         }
22889         this.fireEvent("add", this, records, index);
22890     },
22891
22892     /**
22893      * Get the index within the cache of the passed Record.
22894      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22895      * @return {Number} The index of the passed Record. Returns -1 if not found.
22896      */
22897     indexOf : function(record){
22898         return this.data.indexOf(record);
22899     },
22900
22901     /**
22902      * Get the index within the cache of the Record with the passed id.
22903      * @param {String} id The id of the Record to find.
22904      * @return {Number} The index of the Record. Returns -1 if not found.
22905      */
22906     indexOfId : function(id){
22907         return this.data.indexOfKey(id);
22908     },
22909
22910     /**
22911      * Get the Record with the specified id.
22912      * @param {String} id The id of the Record to find.
22913      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22914      */
22915     getById : function(id){
22916         return this.data.key(id);
22917     },
22918
22919     /**
22920      * Get the Record at the specified index.
22921      * @param {Number} index The index of the Record to find.
22922      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22923      */
22924     getAt : function(index){
22925         return this.data.itemAt(index);
22926     },
22927
22928     /**
22929      * Returns a range of Records between specified indices.
22930      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22931      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22932      * @return {Roo.data.Record[]} An array of Records
22933      */
22934     getRange : function(start, end){
22935         return this.data.getRange(start, end);
22936     },
22937
22938     // private
22939     storeOptions : function(o){
22940         o = Roo.apply({}, o);
22941         delete o.callback;
22942         delete o.scope;
22943         this.lastOptions = o;
22944     },
22945
22946     /**
22947      * Loads the Record cache from the configured Proxy using the configured Reader.
22948      * <p>
22949      * If using remote paging, then the first load call must specify the <em>start</em>
22950      * and <em>limit</em> properties in the options.params property to establish the initial
22951      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22952      * <p>
22953      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22954      * and this call will return before the new data has been loaded. Perform any post-processing
22955      * in a callback function, or in a "load" event handler.</strong>
22956      * <p>
22957      * @param {Object} options An object containing properties which control loading options:<ul>
22958      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22959      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22960      * passed the following arguments:<ul>
22961      * <li>r : Roo.data.Record[]</li>
22962      * <li>options: Options object from the load call</li>
22963      * <li>success: Boolean success indicator</li></ul></li>
22964      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22965      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
22966      * </ul>
22967      */
22968     load : function(options){
22969         options = options || {};
22970         if(this.fireEvent("beforeload", this, options) !== false){
22971             this.storeOptions(options);
22972             var p = Roo.apply(options.params || {}, this.baseParams);
22973             // if meta was not loaded from remote source.. try requesting it.
22974             if (!this.reader.metaFromRemote) {
22975                 p._requestMeta = 1;
22976             }
22977             if(this.sortInfo && this.remoteSort){
22978                 var pn = this.paramNames;
22979                 p[pn["sort"]] = this.sortInfo.field;
22980                 p[pn["dir"]] = this.sortInfo.direction;
22981             }
22982             if (this.multiSort) {
22983                 var pn = this.paramNames;
22984                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
22985             }
22986             
22987             this.proxy.load(p, this.reader, this.loadRecords, this, options);
22988         }
22989     },
22990
22991     /**
22992      * Reloads the Record cache from the configured Proxy using the configured Reader and
22993      * the options from the last load operation performed.
22994      * @param {Object} options (optional) An object containing properties which may override the options
22995      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
22996      * the most recently used options are reused).
22997      */
22998     reload : function(options){
22999         this.load(Roo.applyIf(options||{}, this.lastOptions));
23000     },
23001
23002     // private
23003     // Called as a callback by the Reader during a load operation.
23004     loadRecords : function(o, options, success){
23005         if(!o || success === false){
23006             if(success !== false){
23007                 this.fireEvent("load", this, [], options, o);
23008             }
23009             if(options.callback){
23010                 options.callback.call(options.scope || this, [], options, false);
23011             }
23012             return;
23013         }
23014         // if data returned failure - throw an exception.
23015         if (o.success === false) {
23016             // show a message if no listener is registered.
23017             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23018                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23019             }
23020             // loadmask wil be hooked into this..
23021             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23022             return;
23023         }
23024         var r = o.records, t = o.totalRecords || r.length;
23025         
23026         this.fireEvent("beforeloadadd", this, r, options, o);
23027         
23028         if(!options || options.add !== true){
23029             if(this.pruneModifiedRecords){
23030                 this.modified = [];
23031             }
23032             for(var i = 0, len = r.length; i < len; i++){
23033                 r[i].join(this);
23034             }
23035             if(this.snapshot){
23036                 this.data = this.snapshot;
23037                 delete this.snapshot;
23038             }
23039             this.data.clear();
23040             this.data.addAll(r);
23041             this.totalLength = t;
23042             this.applySort();
23043             this.fireEvent("datachanged", this);
23044         }else{
23045             this.totalLength = Math.max(t, this.data.length+r.length);
23046             this.add(r);
23047         }
23048         this.fireEvent("load", this, r, options, o);
23049         if(options.callback){
23050             options.callback.call(options.scope || this, r, options, true);
23051         }
23052     },
23053
23054
23055     /**
23056      * Loads data from a passed data block. A Reader which understands the format of the data
23057      * must have been configured in the constructor.
23058      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23059      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23060      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23061      */
23062     loadData : function(o, append){
23063         var r = this.reader.readRecords(o);
23064         this.loadRecords(r, {add: append}, true);
23065     },
23066
23067     /**
23068      * Gets the number of cached records.
23069      * <p>
23070      * <em>If using paging, this may not be the total size of the dataset. If the data object
23071      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23072      * the data set size</em>
23073      */
23074     getCount : function(){
23075         return this.data.length || 0;
23076     },
23077
23078     /**
23079      * Gets the total number of records in the dataset as returned by the server.
23080      * <p>
23081      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23082      * the dataset size</em>
23083      */
23084     getTotalCount : function(){
23085         return this.totalLength || 0;
23086     },
23087
23088     /**
23089      * Returns the sort state of the Store as an object with two properties:
23090      * <pre><code>
23091  field {String} The name of the field by which the Records are sorted
23092  direction {String} The sort order, "ASC" or "DESC"
23093      * </code></pre>
23094      */
23095     getSortState : function(){
23096         return this.sortInfo;
23097     },
23098
23099     // private
23100     applySort : function(){
23101         if(this.sortInfo && !this.remoteSort){
23102             var s = this.sortInfo, f = s.field;
23103             var st = this.fields.get(f).sortType;
23104             var fn = function(r1, r2){
23105                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23106                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23107             };
23108             this.data.sort(s.direction, fn);
23109             if(this.snapshot && this.snapshot != this.data){
23110                 this.snapshot.sort(s.direction, fn);
23111             }
23112         }
23113     },
23114
23115     /**
23116      * Sets the default sort column and order to be used by the next load operation.
23117      * @param {String} fieldName The name of the field to sort by.
23118      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23119      */
23120     setDefaultSort : function(field, dir){
23121         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23122     },
23123
23124     /**
23125      * Sort the Records.
23126      * If remote sorting is used, the sort is performed on the server, and the cache is
23127      * reloaded. If local sorting is used, the cache is sorted internally.
23128      * @param {String} fieldName The name of the field to sort by.
23129      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23130      */
23131     sort : function(fieldName, dir){
23132         var f = this.fields.get(fieldName);
23133         if(!dir){
23134             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23135             
23136             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23137                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23138             }else{
23139                 dir = f.sortDir;
23140             }
23141         }
23142         this.sortToggle[f.name] = dir;
23143         this.sortInfo = {field: f.name, direction: dir};
23144         if(!this.remoteSort){
23145             this.applySort();
23146             this.fireEvent("datachanged", this);
23147         }else{
23148             this.load(this.lastOptions);
23149         }
23150     },
23151
23152     /**
23153      * Calls the specified function for each of the Records in the cache.
23154      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23155      * Returning <em>false</em> aborts and exits the iteration.
23156      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23157      */
23158     each : function(fn, scope){
23159         this.data.each(fn, scope);
23160     },
23161
23162     /**
23163      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23164      * (e.g., during paging).
23165      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23166      */
23167     getModifiedRecords : function(){
23168         return this.modified;
23169     },
23170
23171     // private
23172     createFilterFn : function(property, value, anyMatch){
23173         if(!value.exec){ // not a regex
23174             value = String(value);
23175             if(value.length == 0){
23176                 return false;
23177             }
23178             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23179         }
23180         return function(r){
23181             return value.test(r.data[property]);
23182         };
23183     },
23184
23185     /**
23186      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23187      * @param {String} property A field on your records
23188      * @param {Number} start The record index to start at (defaults to 0)
23189      * @param {Number} end The last record index to include (defaults to length - 1)
23190      * @return {Number} The sum
23191      */
23192     sum : function(property, start, end){
23193         var rs = this.data.items, v = 0;
23194         start = start || 0;
23195         end = (end || end === 0) ? end : rs.length-1;
23196
23197         for(var i = start; i <= end; i++){
23198             v += (rs[i].data[property] || 0);
23199         }
23200         return v;
23201     },
23202
23203     /**
23204      * Filter the records by a specified property.
23205      * @param {String} field A field on your records
23206      * @param {String/RegExp} value Either a string that the field
23207      * should start with or a RegExp to test against the field
23208      * @param {Boolean} anyMatch True to match any part not just the beginning
23209      */
23210     filter : function(property, value, anyMatch){
23211         var fn = this.createFilterFn(property, value, anyMatch);
23212         return fn ? this.filterBy(fn) : this.clearFilter();
23213     },
23214
23215     /**
23216      * Filter by a function. The specified function will be called with each
23217      * record in this data source. If the function returns true the record is included,
23218      * otherwise it is filtered.
23219      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23220      * @param {Object} scope (optional) The scope of the function (defaults to this)
23221      */
23222     filterBy : function(fn, scope){
23223         this.snapshot = this.snapshot || this.data;
23224         this.data = this.queryBy(fn, scope||this);
23225         this.fireEvent("datachanged", this);
23226     },
23227
23228     /**
23229      * Query the records by a specified property.
23230      * @param {String} field A field on your records
23231      * @param {String/RegExp} value Either a string that the field
23232      * should start with or a RegExp to test against the field
23233      * @param {Boolean} anyMatch True to match any part not just the beginning
23234      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23235      */
23236     query : function(property, value, anyMatch){
23237         var fn = this.createFilterFn(property, value, anyMatch);
23238         return fn ? this.queryBy(fn) : this.data.clone();
23239     },
23240
23241     /**
23242      * Query by a function. The specified function will be called with each
23243      * record in this data source. If the function returns true the record is included
23244      * in the results.
23245      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23246      * @param {Object} scope (optional) The scope of the function (defaults to this)
23247       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23248      **/
23249     queryBy : function(fn, scope){
23250         var data = this.snapshot || this.data;
23251         return data.filterBy(fn, scope||this);
23252     },
23253
23254     /**
23255      * Collects unique values for a particular dataIndex from this store.
23256      * @param {String} dataIndex The property to collect
23257      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23258      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23259      * @return {Array} An array of the unique values
23260      **/
23261     collect : function(dataIndex, allowNull, bypassFilter){
23262         var d = (bypassFilter === true && this.snapshot) ?
23263                 this.snapshot.items : this.data.items;
23264         var v, sv, r = [], l = {};
23265         for(var i = 0, len = d.length; i < len; i++){
23266             v = d[i].data[dataIndex];
23267             sv = String(v);
23268             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23269                 l[sv] = true;
23270                 r[r.length] = v;
23271             }
23272         }
23273         return r;
23274     },
23275
23276     /**
23277      * Revert to a view of the Record cache with no filtering applied.
23278      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23279      */
23280     clearFilter : function(suppressEvent){
23281         if(this.snapshot && this.snapshot != this.data){
23282             this.data = this.snapshot;
23283             delete this.snapshot;
23284             if(suppressEvent !== true){
23285                 this.fireEvent("datachanged", this);
23286             }
23287         }
23288     },
23289
23290     // private
23291     afterEdit : function(record){
23292         if(this.modified.indexOf(record) == -1){
23293             this.modified.push(record);
23294         }
23295         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23296     },
23297     
23298     // private
23299     afterReject : function(record){
23300         this.modified.remove(record);
23301         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23302     },
23303
23304     // private
23305     afterCommit : function(record){
23306         this.modified.remove(record);
23307         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23308     },
23309
23310     /**
23311      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23312      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23313      */
23314     commitChanges : function(){
23315         var m = this.modified.slice(0);
23316         this.modified = [];
23317         for(var i = 0, len = m.length; i < len; i++){
23318             m[i].commit();
23319         }
23320     },
23321
23322     /**
23323      * Cancel outstanding changes on all changed records.
23324      */
23325     rejectChanges : function(){
23326         var m = this.modified.slice(0);
23327         this.modified = [];
23328         for(var i = 0, len = m.length; i < len; i++){
23329             m[i].reject();
23330         }
23331     },
23332
23333     onMetaChange : function(meta, rtype, o){
23334         this.recordType = rtype;
23335         this.fields = rtype.prototype.fields;
23336         delete this.snapshot;
23337         this.sortInfo = meta.sortInfo || this.sortInfo;
23338         this.modified = [];
23339         this.fireEvent('metachange', this, this.reader.meta);
23340     },
23341     
23342     moveIndex : function(data, type)
23343     {
23344         var index = this.indexOf(data);
23345         
23346         var newIndex = index + type;
23347         
23348         this.remove(data);
23349         
23350         this.insert(newIndex, data);
23351         
23352     }
23353 });/*
23354  * Based on:
23355  * Ext JS Library 1.1.1
23356  * Copyright(c) 2006-2007, Ext JS, LLC.
23357  *
23358  * Originally Released Under LGPL - original licence link has changed is not relivant.
23359  *
23360  * Fork - LGPL
23361  * <script type="text/javascript">
23362  */
23363
23364 /**
23365  * @class Roo.data.SimpleStore
23366  * @extends Roo.data.Store
23367  * Small helper class to make creating Stores from Array data easier.
23368  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23369  * @cfg {Array} fields An array of field definition objects, or field name strings.
23370  * @cfg {Array} data The multi-dimensional array of data
23371  * @constructor
23372  * @param {Object} config
23373  */
23374 Roo.data.SimpleStore = function(config){
23375     Roo.data.SimpleStore.superclass.constructor.call(this, {
23376         isLocal : true,
23377         reader: new Roo.data.ArrayReader({
23378                 id: config.id
23379             },
23380             Roo.data.Record.create(config.fields)
23381         ),
23382         proxy : new Roo.data.MemoryProxy(config.data)
23383     });
23384     this.load();
23385 };
23386 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23387  * Based on:
23388  * Ext JS Library 1.1.1
23389  * Copyright(c) 2006-2007, Ext JS, LLC.
23390  *
23391  * Originally Released Under LGPL - original licence link has changed is not relivant.
23392  *
23393  * Fork - LGPL
23394  * <script type="text/javascript">
23395  */
23396
23397 /**
23398 /**
23399  * @extends Roo.data.Store
23400  * @class Roo.data.JsonStore
23401  * Small helper class to make creating Stores for JSON data easier. <br/>
23402 <pre><code>
23403 var store = new Roo.data.JsonStore({
23404     url: 'get-images.php',
23405     root: 'images',
23406     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23407 });
23408 </code></pre>
23409  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23410  * JsonReader and HttpProxy (unless inline data is provided).</b>
23411  * @cfg {Array} fields An array of field definition objects, or field name strings.
23412  * @constructor
23413  * @param {Object} config
23414  */
23415 Roo.data.JsonStore = function(c){
23416     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23417         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23418         reader: new Roo.data.JsonReader(c, c.fields)
23419     }));
23420 };
23421 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23422  * Based on:
23423  * Ext JS Library 1.1.1
23424  * Copyright(c) 2006-2007, Ext JS, LLC.
23425  *
23426  * Originally Released Under LGPL - original licence link has changed is not relivant.
23427  *
23428  * Fork - LGPL
23429  * <script type="text/javascript">
23430  */
23431
23432  
23433 Roo.data.Field = function(config){
23434     if(typeof config == "string"){
23435         config = {name: config};
23436     }
23437     Roo.apply(this, config);
23438     
23439     if(!this.type){
23440         this.type = "auto";
23441     }
23442     
23443     var st = Roo.data.SortTypes;
23444     // named sortTypes are supported, here we look them up
23445     if(typeof this.sortType == "string"){
23446         this.sortType = st[this.sortType];
23447     }
23448     
23449     // set default sortType for strings and dates
23450     if(!this.sortType){
23451         switch(this.type){
23452             case "string":
23453                 this.sortType = st.asUCString;
23454                 break;
23455             case "date":
23456                 this.sortType = st.asDate;
23457                 break;
23458             default:
23459                 this.sortType = st.none;
23460         }
23461     }
23462
23463     // define once
23464     var stripRe = /[\$,%]/g;
23465
23466     // prebuilt conversion function for this field, instead of
23467     // switching every time we're reading a value
23468     if(!this.convert){
23469         var cv, dateFormat = this.dateFormat;
23470         switch(this.type){
23471             case "":
23472             case "auto":
23473             case undefined:
23474                 cv = function(v){ return v; };
23475                 break;
23476             case "string":
23477                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23478                 break;
23479             case "int":
23480                 cv = function(v){
23481                     return v !== undefined && v !== null && v !== '' ?
23482                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23483                     };
23484                 break;
23485             case "float":
23486                 cv = function(v){
23487                     return v !== undefined && v !== null && v !== '' ?
23488                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23489                     };
23490                 break;
23491             case "bool":
23492             case "boolean":
23493                 cv = function(v){ return v === true || v === "true" || v == 1; };
23494                 break;
23495             case "date":
23496                 cv = function(v){
23497                     if(!v){
23498                         return '';
23499                     }
23500                     if(v instanceof Date){
23501                         return v;
23502                     }
23503                     if(dateFormat){
23504                         if(dateFormat == "timestamp"){
23505                             return new Date(v*1000);
23506                         }
23507                         return Date.parseDate(v, dateFormat);
23508                     }
23509                     var parsed = Date.parse(v);
23510                     return parsed ? new Date(parsed) : null;
23511                 };
23512              break;
23513             
23514         }
23515         this.convert = cv;
23516     }
23517 };
23518
23519 Roo.data.Field.prototype = {
23520     dateFormat: null,
23521     defaultValue: "",
23522     mapping: null,
23523     sortType : null,
23524     sortDir : "ASC"
23525 };/*
23526  * Based on:
23527  * Ext JS Library 1.1.1
23528  * Copyright(c) 2006-2007, Ext JS, LLC.
23529  *
23530  * Originally Released Under LGPL - original licence link has changed is not relivant.
23531  *
23532  * Fork - LGPL
23533  * <script type="text/javascript">
23534  */
23535  
23536 // Base class for reading structured data from a data source.  This class is intended to be
23537 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23538
23539 /**
23540  * @class Roo.data.DataReader
23541  * Base class for reading structured data from a data source.  This class is intended to be
23542  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23543  */
23544
23545 Roo.data.DataReader = function(meta, recordType){
23546     
23547     this.meta = meta;
23548     
23549     this.recordType = recordType instanceof Array ? 
23550         Roo.data.Record.create(recordType) : recordType;
23551 };
23552
23553 Roo.data.DataReader.prototype = {
23554      /**
23555      * Create an empty record
23556      * @param {Object} data (optional) - overlay some values
23557      * @return {Roo.data.Record} record created.
23558      */
23559     newRow :  function(d) {
23560         var da =  {};
23561         this.recordType.prototype.fields.each(function(c) {
23562             switch( c.type) {
23563                 case 'int' : da[c.name] = 0; break;
23564                 case 'date' : da[c.name] = new Date(); break;
23565                 case 'float' : da[c.name] = 0.0; break;
23566                 case 'boolean' : da[c.name] = false; break;
23567                 default : da[c.name] = ""; break;
23568             }
23569             
23570         });
23571         return new this.recordType(Roo.apply(da, d));
23572     }
23573     
23574 };/*
23575  * Based on:
23576  * Ext JS Library 1.1.1
23577  * Copyright(c) 2006-2007, Ext JS, LLC.
23578  *
23579  * Originally Released Under LGPL - original licence link has changed is not relivant.
23580  *
23581  * Fork - LGPL
23582  * <script type="text/javascript">
23583  */
23584
23585 /**
23586  * @class Roo.data.DataProxy
23587  * @extends Roo.data.Observable
23588  * This class is an abstract base class for implementations which provide retrieval of
23589  * unformatted data objects.<br>
23590  * <p>
23591  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23592  * (of the appropriate type which knows how to parse the data object) to provide a block of
23593  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23594  * <p>
23595  * Custom implementations must implement the load method as described in
23596  * {@link Roo.data.HttpProxy#load}.
23597  */
23598 Roo.data.DataProxy = function(){
23599     this.addEvents({
23600         /**
23601          * @event beforeload
23602          * Fires before a network request is made to retrieve a data object.
23603          * @param {Object} This DataProxy object.
23604          * @param {Object} params The params parameter to the load function.
23605          */
23606         beforeload : true,
23607         /**
23608          * @event load
23609          * Fires before the load method's callback is called.
23610          * @param {Object} This DataProxy object.
23611          * @param {Object} o The data object.
23612          * @param {Object} arg The callback argument object passed to the load function.
23613          */
23614         load : true,
23615         /**
23616          * @event loadexception
23617          * Fires if an Exception occurs during data retrieval.
23618          * @param {Object} This DataProxy object.
23619          * @param {Object} o The data object.
23620          * @param {Object} arg The callback argument object passed to the load function.
23621          * @param {Object} e The Exception.
23622          */
23623         loadexception : true
23624     });
23625     Roo.data.DataProxy.superclass.constructor.call(this);
23626 };
23627
23628 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23629
23630     /**
23631      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23632      */
23633 /*
23634  * Based on:
23635  * Ext JS Library 1.1.1
23636  * Copyright(c) 2006-2007, Ext JS, LLC.
23637  *
23638  * Originally Released Under LGPL - original licence link has changed is not relivant.
23639  *
23640  * Fork - LGPL
23641  * <script type="text/javascript">
23642  */
23643 /**
23644  * @class Roo.data.MemoryProxy
23645  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23646  * to the Reader when its load method is called.
23647  * @constructor
23648  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23649  */
23650 Roo.data.MemoryProxy = function(data){
23651     if (data.data) {
23652         data = data.data;
23653     }
23654     Roo.data.MemoryProxy.superclass.constructor.call(this);
23655     this.data = data;
23656 };
23657
23658 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23659     
23660     /**
23661      * Load data from the requested source (in this case an in-memory
23662      * data object passed to the constructor), read the data object into
23663      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23664      * process that block using the passed callback.
23665      * @param {Object} params This parameter is not used by the MemoryProxy class.
23666      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23667      * object into a block of Roo.data.Records.
23668      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23669      * The function must be passed <ul>
23670      * <li>The Record block object</li>
23671      * <li>The "arg" argument from the load function</li>
23672      * <li>A boolean success indicator</li>
23673      * </ul>
23674      * @param {Object} scope The scope in which to call the callback
23675      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23676      */
23677     load : function(params, reader, callback, scope, arg){
23678         params = params || {};
23679         var result;
23680         try {
23681             result = reader.readRecords(this.data);
23682         }catch(e){
23683             this.fireEvent("loadexception", this, arg, null, e);
23684             callback.call(scope, null, arg, false);
23685             return;
23686         }
23687         callback.call(scope, result, arg, true);
23688     },
23689     
23690     // private
23691     update : function(params, records){
23692         
23693     }
23694 });/*
23695  * Based on:
23696  * Ext JS Library 1.1.1
23697  * Copyright(c) 2006-2007, Ext JS, LLC.
23698  *
23699  * Originally Released Under LGPL - original licence link has changed is not relivant.
23700  *
23701  * Fork - LGPL
23702  * <script type="text/javascript">
23703  */
23704 /**
23705  * @class Roo.data.HttpProxy
23706  * @extends Roo.data.DataProxy
23707  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23708  * configured to reference a certain URL.<br><br>
23709  * <p>
23710  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23711  * from which the running page was served.<br><br>
23712  * <p>
23713  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23714  * <p>
23715  * Be aware that to enable the browser to parse an XML document, the server must set
23716  * the Content-Type header in the HTTP response to "text/xml".
23717  * @constructor
23718  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23719  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23720  * will be used to make the request.
23721  */
23722 Roo.data.HttpProxy = function(conn){
23723     Roo.data.HttpProxy.superclass.constructor.call(this);
23724     // is conn a conn config or a real conn?
23725     this.conn = conn;
23726     this.useAjax = !conn || !conn.events;
23727   
23728 };
23729
23730 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23731     // thse are take from connection...
23732     
23733     /**
23734      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23735      */
23736     /**
23737      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23738      * extra parameters to each request made by this object. (defaults to undefined)
23739      */
23740     /**
23741      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23742      *  to each request made by this object. (defaults to undefined)
23743      */
23744     /**
23745      * @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)
23746      */
23747     /**
23748      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23749      */
23750      /**
23751      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23752      * @type Boolean
23753      */
23754   
23755
23756     /**
23757      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23758      * @type Boolean
23759      */
23760     /**
23761      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23762      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23763      * a finer-grained basis than the DataProxy events.
23764      */
23765     getConnection : function(){
23766         return this.useAjax ? Roo.Ajax : this.conn;
23767     },
23768
23769     /**
23770      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23771      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23772      * process that block using the passed callback.
23773      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23774      * for the request to the remote server.
23775      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23776      * object into a block of Roo.data.Records.
23777      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23778      * The function must be passed <ul>
23779      * <li>The Record block object</li>
23780      * <li>The "arg" argument from the load function</li>
23781      * <li>A boolean success indicator</li>
23782      * </ul>
23783      * @param {Object} scope The scope in which to call the callback
23784      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23785      */
23786     load : function(params, reader, callback, scope, arg){
23787         if(this.fireEvent("beforeload", this, params) !== false){
23788             var  o = {
23789                 params : params || {},
23790                 request: {
23791                     callback : callback,
23792                     scope : scope,
23793                     arg : arg
23794                 },
23795                 reader: reader,
23796                 callback : this.loadResponse,
23797                 scope: this
23798             };
23799             if(this.useAjax){
23800                 Roo.applyIf(o, this.conn);
23801                 if(this.activeRequest){
23802                     Roo.Ajax.abort(this.activeRequest);
23803                 }
23804                 this.activeRequest = Roo.Ajax.request(o);
23805             }else{
23806                 this.conn.request(o);
23807             }
23808         }else{
23809             callback.call(scope||this, null, arg, false);
23810         }
23811     },
23812
23813     // private
23814     loadResponse : function(o, success, response){
23815         delete this.activeRequest;
23816         if(!success){
23817             this.fireEvent("loadexception", this, o, response);
23818             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23819             return;
23820         }
23821         var result;
23822         try {
23823             result = o.reader.read(response);
23824         }catch(e){
23825             this.fireEvent("loadexception", this, o, response, e);
23826             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23827             return;
23828         }
23829         
23830         this.fireEvent("load", this, o, o.request.arg);
23831         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23832     },
23833
23834     // private
23835     update : function(dataSet){
23836
23837     },
23838
23839     // private
23840     updateResponse : function(dataSet){
23841
23842     }
23843 });/*
23844  * Based on:
23845  * Ext JS Library 1.1.1
23846  * Copyright(c) 2006-2007, Ext JS, LLC.
23847  *
23848  * Originally Released Under LGPL - original licence link has changed is not relivant.
23849  *
23850  * Fork - LGPL
23851  * <script type="text/javascript">
23852  */
23853
23854 /**
23855  * @class Roo.data.ScriptTagProxy
23856  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23857  * other than the originating domain of the running page.<br><br>
23858  * <p>
23859  * <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
23860  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23861  * <p>
23862  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23863  * source code that is used as the source inside a &lt;script> tag.<br><br>
23864  * <p>
23865  * In order for the browser to process the returned data, the server must wrap the data object
23866  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23867  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23868  * depending on whether the callback name was passed:
23869  * <p>
23870  * <pre><code>
23871 boolean scriptTag = false;
23872 String cb = request.getParameter("callback");
23873 if (cb != null) {
23874     scriptTag = true;
23875     response.setContentType("text/javascript");
23876 } else {
23877     response.setContentType("application/x-json");
23878 }
23879 Writer out = response.getWriter();
23880 if (scriptTag) {
23881     out.write(cb + "(");
23882 }
23883 out.print(dataBlock.toJsonString());
23884 if (scriptTag) {
23885     out.write(");");
23886 }
23887 </pre></code>
23888  *
23889  * @constructor
23890  * @param {Object} config A configuration object.
23891  */
23892 Roo.data.ScriptTagProxy = function(config){
23893     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23894     Roo.apply(this, config);
23895     this.head = document.getElementsByTagName("head")[0];
23896 };
23897
23898 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23899
23900 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23901     /**
23902      * @cfg {String} url The URL from which to request the data object.
23903      */
23904     /**
23905      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23906      */
23907     timeout : 30000,
23908     /**
23909      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23910      * the server the name of the callback function set up by the load call to process the returned data object.
23911      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23912      * javascript output which calls this named function passing the data object as its only parameter.
23913      */
23914     callbackParam : "callback",
23915     /**
23916      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23917      * name to the request.
23918      */
23919     nocache : true,
23920
23921     /**
23922      * Load data from the configured URL, read the data object into
23923      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23924      * process that block using the passed callback.
23925      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23926      * for the request to the remote server.
23927      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23928      * object into a block of Roo.data.Records.
23929      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23930      * The function must be passed <ul>
23931      * <li>The Record block object</li>
23932      * <li>The "arg" argument from the load function</li>
23933      * <li>A boolean success indicator</li>
23934      * </ul>
23935      * @param {Object} scope The scope in which to call the callback
23936      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23937      */
23938     load : function(params, reader, callback, scope, arg){
23939         if(this.fireEvent("beforeload", this, params) !== false){
23940
23941             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23942
23943             var url = this.url;
23944             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23945             if(this.nocache){
23946                 url += "&_dc=" + (new Date().getTime());
23947             }
23948             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23949             var trans = {
23950                 id : transId,
23951                 cb : "stcCallback"+transId,
23952                 scriptId : "stcScript"+transId,
23953                 params : params,
23954                 arg : arg,
23955                 url : url,
23956                 callback : callback,
23957                 scope : scope,
23958                 reader : reader
23959             };
23960             var conn = this;
23961
23962             window[trans.cb] = function(o){
23963                 conn.handleResponse(o, trans);
23964             };
23965
23966             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
23967
23968             if(this.autoAbort !== false){
23969                 this.abort();
23970             }
23971
23972             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
23973
23974             var script = document.createElement("script");
23975             script.setAttribute("src", url);
23976             script.setAttribute("type", "text/javascript");
23977             script.setAttribute("id", trans.scriptId);
23978             this.head.appendChild(script);
23979
23980             this.trans = trans;
23981         }else{
23982             callback.call(scope||this, null, arg, false);
23983         }
23984     },
23985
23986     // private
23987     isLoading : function(){
23988         return this.trans ? true : false;
23989     },
23990
23991     /**
23992      * Abort the current server request.
23993      */
23994     abort : function(){
23995         if(this.isLoading()){
23996             this.destroyTrans(this.trans);
23997         }
23998     },
23999
24000     // private
24001     destroyTrans : function(trans, isLoaded){
24002         this.head.removeChild(document.getElementById(trans.scriptId));
24003         clearTimeout(trans.timeoutId);
24004         if(isLoaded){
24005             window[trans.cb] = undefined;
24006             try{
24007                 delete window[trans.cb];
24008             }catch(e){}
24009         }else{
24010             // if hasn't been loaded, wait for load to remove it to prevent script error
24011             window[trans.cb] = function(){
24012                 window[trans.cb] = undefined;
24013                 try{
24014                     delete window[trans.cb];
24015                 }catch(e){}
24016             };
24017         }
24018     },
24019
24020     // private
24021     handleResponse : function(o, trans){
24022         this.trans = false;
24023         this.destroyTrans(trans, true);
24024         var result;
24025         try {
24026             result = trans.reader.readRecords(o);
24027         }catch(e){
24028             this.fireEvent("loadexception", this, o, trans.arg, e);
24029             trans.callback.call(trans.scope||window, null, trans.arg, false);
24030             return;
24031         }
24032         this.fireEvent("load", this, o, trans.arg);
24033         trans.callback.call(trans.scope||window, result, trans.arg, true);
24034     },
24035
24036     // private
24037     handleFailure : function(trans){
24038         this.trans = false;
24039         this.destroyTrans(trans, false);
24040         this.fireEvent("loadexception", this, null, trans.arg);
24041         trans.callback.call(trans.scope||window, null, trans.arg, false);
24042     }
24043 });/*
24044  * Based on:
24045  * Ext JS Library 1.1.1
24046  * Copyright(c) 2006-2007, Ext JS, LLC.
24047  *
24048  * Originally Released Under LGPL - original licence link has changed is not relivant.
24049  *
24050  * Fork - LGPL
24051  * <script type="text/javascript">
24052  */
24053
24054 /**
24055  * @class Roo.data.JsonReader
24056  * @extends Roo.data.DataReader
24057  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24058  * based on mappings in a provided Roo.data.Record constructor.
24059  * 
24060  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24061  * in the reply previously. 
24062  * 
24063  * <p>
24064  * Example code:
24065  * <pre><code>
24066 var RecordDef = Roo.data.Record.create([
24067     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24068     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24069 ]);
24070 var myReader = new Roo.data.JsonReader({
24071     totalProperty: "results",    // The property which contains the total dataset size (optional)
24072     root: "rows",                // The property which contains an Array of row objects
24073     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24074 }, RecordDef);
24075 </code></pre>
24076  * <p>
24077  * This would consume a JSON file like this:
24078  * <pre><code>
24079 { 'results': 2, 'rows': [
24080     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24081     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24082 }
24083 </code></pre>
24084  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24085  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24086  * paged from the remote server.
24087  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24088  * @cfg {String} root name of the property which contains the Array of row objects.
24089  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24090  * @cfg {Array} fields Array of field definition objects
24091  * @constructor
24092  * Create a new JsonReader
24093  * @param {Object} meta Metadata configuration options
24094  * @param {Object} recordType Either an Array of field definition objects,
24095  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24096  */
24097 Roo.data.JsonReader = function(meta, recordType){
24098     
24099     meta = meta || {};
24100     // set some defaults:
24101     Roo.applyIf(meta, {
24102         totalProperty: 'total',
24103         successProperty : 'success',
24104         root : 'data',
24105         id : 'id'
24106     });
24107     
24108     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24109 };
24110 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24111     
24112     /**
24113      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24114      * Used by Store query builder to append _requestMeta to params.
24115      * 
24116      */
24117     metaFromRemote : false,
24118     /**
24119      * This method is only used by a DataProxy which has retrieved data from a remote server.
24120      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24121      * @return {Object} data A data block which is used by an Roo.data.Store object as
24122      * a cache of Roo.data.Records.
24123      */
24124     read : function(response){
24125         var json = response.responseText;
24126        
24127         var o = /* eval:var:o */ eval("("+json+")");
24128         if(!o) {
24129             throw {message: "JsonReader.read: Json object not found"};
24130         }
24131         
24132         if(o.metaData){
24133             
24134             delete this.ef;
24135             this.metaFromRemote = true;
24136             this.meta = o.metaData;
24137             this.recordType = Roo.data.Record.create(o.metaData.fields);
24138             this.onMetaChange(this.meta, this.recordType, o);
24139         }
24140         return this.readRecords(o);
24141     },
24142
24143     // private function a store will implement
24144     onMetaChange : function(meta, recordType, o){
24145
24146     },
24147
24148     /**
24149          * @ignore
24150          */
24151     simpleAccess: function(obj, subsc) {
24152         return obj[subsc];
24153     },
24154
24155         /**
24156          * @ignore
24157          */
24158     getJsonAccessor: function(){
24159         var re = /[\[\.]/;
24160         return function(expr) {
24161             try {
24162                 return(re.test(expr))
24163                     ? new Function("obj", "return obj." + expr)
24164                     : function(obj){
24165                         return obj[expr];
24166                     };
24167             } catch(e){}
24168             return Roo.emptyFn;
24169         };
24170     }(),
24171
24172     /**
24173      * Create a data block containing Roo.data.Records from an XML document.
24174      * @param {Object} o An object which contains an Array of row objects in the property specified
24175      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24176      * which contains the total size of the dataset.
24177      * @return {Object} data A data block which is used by an Roo.data.Store object as
24178      * a cache of Roo.data.Records.
24179      */
24180     readRecords : function(o){
24181         /**
24182          * After any data loads, the raw JSON data is available for further custom processing.
24183          * @type Object
24184          */
24185         this.o = o;
24186         var s = this.meta, Record = this.recordType,
24187             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24188
24189 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24190         if (!this.ef) {
24191             if(s.totalProperty) {
24192                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24193                 }
24194                 if(s.successProperty) {
24195                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24196                 }
24197                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24198                 if (s.id) {
24199                         var g = this.getJsonAccessor(s.id);
24200                         this.getId = function(rec) {
24201                                 var r = g(rec);  
24202                                 return (r === undefined || r === "") ? null : r;
24203                         };
24204                 } else {
24205                         this.getId = function(){return null;};
24206                 }
24207             this.ef = [];
24208             for(var jj = 0; jj < fl; jj++){
24209                 f = fi[jj];
24210                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24211                 this.ef[jj] = this.getJsonAccessor(map);
24212             }
24213         }
24214
24215         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24216         if(s.totalProperty){
24217             var vt = parseInt(this.getTotal(o), 10);
24218             if(!isNaN(vt)){
24219                 totalRecords = vt;
24220             }
24221         }
24222         if(s.successProperty){
24223             var vs = this.getSuccess(o);
24224             if(vs === false || vs === 'false'){
24225                 success = false;
24226             }
24227         }
24228         var records = [];
24229         for(var i = 0; i < c; i++){
24230                 var n = root[i];
24231             var values = {};
24232             var id = this.getId(n);
24233             for(var j = 0; j < fl; j++){
24234                 f = fi[j];
24235             var v = this.ef[j](n);
24236             if (!f.convert) {
24237                 Roo.log('missing convert for ' + f.name);
24238                 Roo.log(f);
24239                 continue;
24240             }
24241             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24242             }
24243             var record = new Record(values, id);
24244             record.json = n;
24245             records[i] = record;
24246         }
24247         return {
24248             raw : o,
24249             success : success,
24250             records : records,
24251             totalRecords : totalRecords
24252         };
24253     }
24254 });/*
24255  * Based on:
24256  * Ext JS Library 1.1.1
24257  * Copyright(c) 2006-2007, Ext JS, LLC.
24258  *
24259  * Originally Released Under LGPL - original licence link has changed is not relivant.
24260  *
24261  * Fork - LGPL
24262  * <script type="text/javascript">
24263  */
24264
24265 /**
24266  * @class Roo.data.XmlReader
24267  * @extends Roo.data.DataReader
24268  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24269  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24270  * <p>
24271  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24272  * header in the HTTP response must be set to "text/xml".</em>
24273  * <p>
24274  * Example code:
24275  * <pre><code>
24276 var RecordDef = Roo.data.Record.create([
24277    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24278    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24279 ]);
24280 var myReader = new Roo.data.XmlReader({
24281    totalRecords: "results", // The element which contains the total dataset size (optional)
24282    record: "row",           // The repeated element which contains row information
24283    id: "id"                 // The element within the row that provides an ID for the record (optional)
24284 }, RecordDef);
24285 </code></pre>
24286  * <p>
24287  * This would consume an XML file like this:
24288  * <pre><code>
24289 &lt;?xml?>
24290 &lt;dataset>
24291  &lt;results>2&lt;/results>
24292  &lt;row>
24293    &lt;id>1&lt;/id>
24294    &lt;name>Bill&lt;/name>
24295    &lt;occupation>Gardener&lt;/occupation>
24296  &lt;/row>
24297  &lt;row>
24298    &lt;id>2&lt;/id>
24299    &lt;name>Ben&lt;/name>
24300    &lt;occupation>Horticulturalist&lt;/occupation>
24301  &lt;/row>
24302 &lt;/dataset>
24303 </code></pre>
24304  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24305  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24306  * paged from the remote server.
24307  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24308  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24309  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24310  * a record identifier value.
24311  * @constructor
24312  * Create a new XmlReader
24313  * @param {Object} meta Metadata configuration options
24314  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24315  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24316  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24317  */
24318 Roo.data.XmlReader = function(meta, recordType){
24319     meta = meta || {};
24320     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24321 };
24322 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24323     /**
24324      * This method is only used by a DataProxy which has retrieved data from a remote server.
24325          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24326          * to contain a method called 'responseXML' that returns an XML document object.
24327      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24328      * a cache of Roo.data.Records.
24329      */
24330     read : function(response){
24331         var doc = response.responseXML;
24332         if(!doc) {
24333             throw {message: "XmlReader.read: XML Document not available"};
24334         }
24335         return this.readRecords(doc);
24336     },
24337
24338     /**
24339      * Create a data block containing Roo.data.Records from an XML document.
24340          * @param {Object} doc A parsed XML document.
24341      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24342      * a cache of Roo.data.Records.
24343      */
24344     readRecords : function(doc){
24345         /**
24346          * After any data loads/reads, the raw XML Document is available for further custom processing.
24347          * @type XMLDocument
24348          */
24349         this.xmlData = doc;
24350         var root = doc.documentElement || doc;
24351         var q = Roo.DomQuery;
24352         var recordType = this.recordType, fields = recordType.prototype.fields;
24353         var sid = this.meta.id;
24354         var totalRecords = 0, success = true;
24355         if(this.meta.totalRecords){
24356             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24357         }
24358         
24359         if(this.meta.success){
24360             var sv = q.selectValue(this.meta.success, root, true);
24361             success = sv !== false && sv !== 'false';
24362         }
24363         var records = [];
24364         var ns = q.select(this.meta.record, root);
24365         for(var i = 0, len = ns.length; i < len; i++) {
24366                 var n = ns[i];
24367                 var values = {};
24368                 var id = sid ? q.selectValue(sid, n) : undefined;
24369                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24370                     var f = fields.items[j];
24371                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24372                     v = f.convert(v);
24373                     values[f.name] = v;
24374                 }
24375                 var record = new recordType(values, id);
24376                 record.node = n;
24377                 records[records.length] = record;
24378             }
24379
24380             return {
24381                 success : success,
24382                 records : records,
24383                 totalRecords : totalRecords || records.length
24384             };
24385     }
24386 });/*
24387  * Based on:
24388  * Ext JS Library 1.1.1
24389  * Copyright(c) 2006-2007, Ext JS, LLC.
24390  *
24391  * Originally Released Under LGPL - original licence link has changed is not relivant.
24392  *
24393  * Fork - LGPL
24394  * <script type="text/javascript">
24395  */
24396
24397 /**
24398  * @class Roo.data.ArrayReader
24399  * @extends Roo.data.DataReader
24400  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24401  * Each element of that Array represents a row of data fields. The
24402  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24403  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24404  * <p>
24405  * Example code:.
24406  * <pre><code>
24407 var RecordDef = Roo.data.Record.create([
24408     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24409     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24410 ]);
24411 var myReader = new Roo.data.ArrayReader({
24412     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24413 }, RecordDef);
24414 </code></pre>
24415  * <p>
24416  * This would consume an Array like this:
24417  * <pre><code>
24418 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24419   </code></pre>
24420  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24421  * @constructor
24422  * Create a new JsonReader
24423  * @param {Object} meta Metadata configuration options.
24424  * @param {Object} recordType Either an Array of field definition objects
24425  * as specified to {@link Roo.data.Record#create},
24426  * or an {@link Roo.data.Record} object
24427  * created using {@link Roo.data.Record#create}.
24428  */
24429 Roo.data.ArrayReader = function(meta, recordType){
24430     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24431 };
24432
24433 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24434     /**
24435      * Create a data block containing Roo.data.Records from an XML document.
24436      * @param {Object} o An Array of row objects which represents the dataset.
24437      * @return {Object} data A data block which is used by an Roo.data.Store object as
24438      * a cache of Roo.data.Records.
24439      */
24440     readRecords : function(o){
24441         var sid = this.meta ? this.meta.id : null;
24442         var recordType = this.recordType, fields = recordType.prototype.fields;
24443         var records = [];
24444         var root = o;
24445             for(var i = 0; i < root.length; i++){
24446                     var n = root[i];
24447                 var values = {};
24448                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24450                 var f = fields.items[j];
24451                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24452                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24453                 v = f.convert(v);
24454                 values[f.name] = v;
24455             }
24456                 var record = new recordType(values, id);
24457                 record.json = n;
24458                 records[records.length] = record;
24459             }
24460             return {
24461                 records : records,
24462                 totalRecords : records.length
24463             };
24464     }
24465 });/*
24466  * Based on:
24467  * Ext JS Library 1.1.1
24468  * Copyright(c) 2006-2007, Ext JS, LLC.
24469  *
24470  * Originally Released Under LGPL - original licence link has changed is not relivant.
24471  *
24472  * Fork - LGPL
24473  * <script type="text/javascript">
24474  */
24475
24476
24477 /**
24478  * @class Roo.data.Tree
24479  * @extends Roo.util.Observable
24480  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24481  * in the tree have most standard DOM functionality.
24482  * @constructor
24483  * @param {Node} root (optional) The root node
24484  */
24485 Roo.data.Tree = function(root){
24486    this.nodeHash = {};
24487    /**
24488     * The root node for this tree
24489     * @type Node
24490     */
24491    this.root = null;
24492    if(root){
24493        this.setRootNode(root);
24494    }
24495    this.addEvents({
24496        /**
24497         * @event append
24498         * Fires when a new child node is appended to a node in this tree.
24499         * @param {Tree} tree The owner tree
24500         * @param {Node} parent The parent node
24501         * @param {Node} node The newly appended node
24502         * @param {Number} index The index of the newly appended node
24503         */
24504        "append" : true,
24505        /**
24506         * @event remove
24507         * Fires when a child node is removed from a node in this tree.
24508         * @param {Tree} tree The owner tree
24509         * @param {Node} parent The parent node
24510         * @param {Node} node The child node removed
24511         */
24512        "remove" : true,
24513        /**
24514         * @event move
24515         * Fires when a node is moved to a new location in the tree
24516         * @param {Tree} tree The owner tree
24517         * @param {Node} node The node moved
24518         * @param {Node} oldParent The old parent of this node
24519         * @param {Node} newParent The new parent of this node
24520         * @param {Number} index The index it was moved to
24521         */
24522        "move" : true,
24523        /**
24524         * @event insert
24525         * Fires when a new child node is inserted in a node in this tree.
24526         * @param {Tree} tree The owner tree
24527         * @param {Node} parent The parent node
24528         * @param {Node} node The child node inserted
24529         * @param {Node} refNode The child node the node was inserted before
24530         */
24531        "insert" : true,
24532        /**
24533         * @event beforeappend
24534         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24535         * @param {Tree} tree The owner tree
24536         * @param {Node} parent The parent node
24537         * @param {Node} node The child node to be appended
24538         */
24539        "beforeappend" : true,
24540        /**
24541         * @event beforeremove
24542         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24543         * @param {Tree} tree The owner tree
24544         * @param {Node} parent The parent node
24545         * @param {Node} node The child node to be removed
24546         */
24547        "beforeremove" : true,
24548        /**
24549         * @event beforemove
24550         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24551         * @param {Tree} tree The owner tree
24552         * @param {Node} node The node being moved
24553         * @param {Node} oldParent The parent of the node
24554         * @param {Node} newParent The new parent the node is moving to
24555         * @param {Number} index The index it is being moved to
24556         */
24557        "beforemove" : true,
24558        /**
24559         * @event beforeinsert
24560         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24561         * @param {Tree} tree The owner tree
24562         * @param {Node} parent The parent node
24563         * @param {Node} node The child node to be inserted
24564         * @param {Node} refNode The child node the node is being inserted before
24565         */
24566        "beforeinsert" : true
24567    });
24568
24569     Roo.data.Tree.superclass.constructor.call(this);
24570 };
24571
24572 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24573     pathSeparator: "/",
24574
24575     proxyNodeEvent : function(){
24576         return this.fireEvent.apply(this, arguments);
24577     },
24578
24579     /**
24580      * Returns the root node for this tree.
24581      * @return {Node}
24582      */
24583     getRootNode : function(){
24584         return this.root;
24585     },
24586
24587     /**
24588      * Sets the root node for this tree.
24589      * @param {Node} node
24590      * @return {Node}
24591      */
24592     setRootNode : function(node){
24593         this.root = node;
24594         node.ownerTree = this;
24595         node.isRoot = true;
24596         this.registerNode(node);
24597         return node;
24598     },
24599
24600     /**
24601      * Gets a node in this tree by its id.
24602      * @param {String} id
24603      * @return {Node}
24604      */
24605     getNodeById : function(id){
24606         return this.nodeHash[id];
24607     },
24608
24609     registerNode : function(node){
24610         this.nodeHash[node.id] = node;
24611     },
24612
24613     unregisterNode : function(node){
24614         delete this.nodeHash[node.id];
24615     },
24616
24617     toString : function(){
24618         return "[Tree"+(this.id?" "+this.id:"")+"]";
24619     }
24620 });
24621
24622 /**
24623  * @class Roo.data.Node
24624  * @extends Roo.util.Observable
24625  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24626  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24627  * @constructor
24628  * @param {Object} attributes The attributes/config for the node
24629  */
24630 Roo.data.Node = function(attributes){
24631     /**
24632      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24633      * @type {Object}
24634      */
24635     this.attributes = attributes || {};
24636     this.leaf = this.attributes.leaf;
24637     /**
24638      * The node id. @type String
24639      */
24640     this.id = this.attributes.id;
24641     if(!this.id){
24642         this.id = Roo.id(null, "ynode-");
24643         this.attributes.id = this.id;
24644     }
24645      
24646     
24647     /**
24648      * All child nodes of this node. @type Array
24649      */
24650     this.childNodes = [];
24651     if(!this.childNodes.indexOf){ // indexOf is a must
24652         this.childNodes.indexOf = function(o){
24653             for(var i = 0, len = this.length; i < len; i++){
24654                 if(this[i] == o) {
24655                     return i;
24656                 }
24657             }
24658             return -1;
24659         };
24660     }
24661     /**
24662      * The parent node for this node. @type Node
24663      */
24664     this.parentNode = null;
24665     /**
24666      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24667      */
24668     this.firstChild = null;
24669     /**
24670      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24671      */
24672     this.lastChild = null;
24673     /**
24674      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24675      */
24676     this.previousSibling = null;
24677     /**
24678      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24679      */
24680     this.nextSibling = null;
24681
24682     this.addEvents({
24683        /**
24684         * @event append
24685         * Fires when a new child node is appended
24686         * @param {Tree} tree The owner tree
24687         * @param {Node} this This node
24688         * @param {Node} node The newly appended node
24689         * @param {Number} index The index of the newly appended node
24690         */
24691        "append" : true,
24692        /**
24693         * @event remove
24694         * Fires when a child node is removed
24695         * @param {Tree} tree The owner tree
24696         * @param {Node} this This node
24697         * @param {Node} node The removed node
24698         */
24699        "remove" : true,
24700        /**
24701         * @event move
24702         * Fires when this node is moved to a new location in the tree
24703         * @param {Tree} tree The owner tree
24704         * @param {Node} this This node
24705         * @param {Node} oldParent The old parent of this node
24706         * @param {Node} newParent The new parent of this node
24707         * @param {Number} index The index it was moved to
24708         */
24709        "move" : true,
24710        /**
24711         * @event insert
24712         * Fires when a new child node is inserted.
24713         * @param {Tree} tree The owner tree
24714         * @param {Node} this This node
24715         * @param {Node} node The child node inserted
24716         * @param {Node} refNode The child node the node was inserted before
24717         */
24718        "insert" : true,
24719        /**
24720         * @event beforeappend
24721         * Fires before a new child is appended, return false to cancel the append.
24722         * @param {Tree} tree The owner tree
24723         * @param {Node} this This node
24724         * @param {Node} node The child node to be appended
24725         */
24726        "beforeappend" : true,
24727        /**
24728         * @event beforeremove
24729         * Fires before a child is removed, return false to cancel the remove.
24730         * @param {Tree} tree The owner tree
24731         * @param {Node} this This node
24732         * @param {Node} node The child node to be removed
24733         */
24734        "beforeremove" : true,
24735        /**
24736         * @event beforemove
24737         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24738         * @param {Tree} tree The owner tree
24739         * @param {Node} this This node
24740         * @param {Node} oldParent The parent of this node
24741         * @param {Node} newParent The new parent this node is moving to
24742         * @param {Number} index The index it is being moved to
24743         */
24744        "beforemove" : true,
24745        /**
24746         * @event beforeinsert
24747         * Fires before a new child is inserted, return false to cancel the insert.
24748         * @param {Tree} tree The owner tree
24749         * @param {Node} this This node
24750         * @param {Node} node The child node to be inserted
24751         * @param {Node} refNode The child node the node is being inserted before
24752         */
24753        "beforeinsert" : true
24754    });
24755     this.listeners = this.attributes.listeners;
24756     Roo.data.Node.superclass.constructor.call(this);
24757 };
24758
24759 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24760     fireEvent : function(evtName){
24761         // first do standard event for this node
24762         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24763             return false;
24764         }
24765         // then bubble it up to the tree if the event wasn't cancelled
24766         var ot = this.getOwnerTree();
24767         if(ot){
24768             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24769                 return false;
24770             }
24771         }
24772         return true;
24773     },
24774
24775     /**
24776      * Returns true if this node is a leaf
24777      * @return {Boolean}
24778      */
24779     isLeaf : function(){
24780         return this.leaf === true;
24781     },
24782
24783     // private
24784     setFirstChild : function(node){
24785         this.firstChild = node;
24786     },
24787
24788     //private
24789     setLastChild : function(node){
24790         this.lastChild = node;
24791     },
24792
24793
24794     /**
24795      * Returns true if this node is the last child of its parent
24796      * @return {Boolean}
24797      */
24798     isLast : function(){
24799        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24800     },
24801
24802     /**
24803      * Returns true if this node is the first child of its parent
24804      * @return {Boolean}
24805      */
24806     isFirst : function(){
24807        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24808     },
24809
24810     hasChildNodes : function(){
24811         return !this.isLeaf() && this.childNodes.length > 0;
24812     },
24813
24814     /**
24815      * Insert node(s) as the last child node of this node.
24816      * @param {Node/Array} node The node or Array of nodes to append
24817      * @return {Node} The appended node if single append, or null if an array was passed
24818      */
24819     appendChild : function(node){
24820         var multi = false;
24821         if(node instanceof Array){
24822             multi = node;
24823         }else if(arguments.length > 1){
24824             multi = arguments;
24825         }
24826         // if passed an array or multiple args do them one by one
24827         if(multi){
24828             for(var i = 0, len = multi.length; i < len; i++) {
24829                 this.appendChild(multi[i]);
24830             }
24831         }else{
24832             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24833                 return false;
24834             }
24835             var index = this.childNodes.length;
24836             var oldParent = node.parentNode;
24837             // it's a move, make sure we move it cleanly
24838             if(oldParent){
24839                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24840                     return false;
24841                 }
24842                 oldParent.removeChild(node);
24843             }
24844             index = this.childNodes.length;
24845             if(index == 0){
24846                 this.setFirstChild(node);
24847             }
24848             this.childNodes.push(node);
24849             node.parentNode = this;
24850             var ps = this.childNodes[index-1];
24851             if(ps){
24852                 node.previousSibling = ps;
24853                 ps.nextSibling = node;
24854             }else{
24855                 node.previousSibling = null;
24856             }
24857             node.nextSibling = null;
24858             this.setLastChild(node);
24859             node.setOwnerTree(this.getOwnerTree());
24860             this.fireEvent("append", this.ownerTree, this, node, index);
24861             if(oldParent){
24862                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24863             }
24864             return node;
24865         }
24866     },
24867
24868     /**
24869      * Removes a child node from this node.
24870      * @param {Node} node The node to remove
24871      * @return {Node} The removed node
24872      */
24873     removeChild : function(node){
24874         var index = this.childNodes.indexOf(node);
24875         if(index == -1){
24876             return false;
24877         }
24878         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24879             return false;
24880         }
24881
24882         // remove it from childNodes collection
24883         this.childNodes.splice(index, 1);
24884
24885         // update siblings
24886         if(node.previousSibling){
24887             node.previousSibling.nextSibling = node.nextSibling;
24888         }
24889         if(node.nextSibling){
24890             node.nextSibling.previousSibling = node.previousSibling;
24891         }
24892
24893         // update child refs
24894         if(this.firstChild == node){
24895             this.setFirstChild(node.nextSibling);
24896         }
24897         if(this.lastChild == node){
24898             this.setLastChild(node.previousSibling);
24899         }
24900
24901         node.setOwnerTree(null);
24902         // clear any references from the node
24903         node.parentNode = null;
24904         node.previousSibling = null;
24905         node.nextSibling = null;
24906         this.fireEvent("remove", this.ownerTree, this, node);
24907         return node;
24908     },
24909
24910     /**
24911      * Inserts the first node before the second node in this nodes childNodes collection.
24912      * @param {Node} node The node to insert
24913      * @param {Node} refNode The node to insert before (if null the node is appended)
24914      * @return {Node} The inserted node
24915      */
24916     insertBefore : function(node, refNode){
24917         if(!refNode){ // like standard Dom, refNode can be null for append
24918             return this.appendChild(node);
24919         }
24920         // nothing to do
24921         if(node == refNode){
24922             return false;
24923         }
24924
24925         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24926             return false;
24927         }
24928         var index = this.childNodes.indexOf(refNode);
24929         var oldParent = node.parentNode;
24930         var refIndex = index;
24931
24932         // when moving internally, indexes will change after remove
24933         if(oldParent == this && this.childNodes.indexOf(node) < index){
24934             refIndex--;
24935         }
24936
24937         // it's a move, make sure we move it cleanly
24938         if(oldParent){
24939             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24940                 return false;
24941             }
24942             oldParent.removeChild(node);
24943         }
24944         if(refIndex == 0){
24945             this.setFirstChild(node);
24946         }
24947         this.childNodes.splice(refIndex, 0, node);
24948         node.parentNode = this;
24949         var ps = this.childNodes[refIndex-1];
24950         if(ps){
24951             node.previousSibling = ps;
24952             ps.nextSibling = node;
24953         }else{
24954             node.previousSibling = null;
24955         }
24956         node.nextSibling = refNode;
24957         refNode.previousSibling = node;
24958         node.setOwnerTree(this.getOwnerTree());
24959         this.fireEvent("insert", this.ownerTree, this, node, refNode);
24960         if(oldParent){
24961             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
24962         }
24963         return node;
24964     },
24965
24966     /**
24967      * Returns the child node at the specified index.
24968      * @param {Number} index
24969      * @return {Node}
24970      */
24971     item : function(index){
24972         return this.childNodes[index];
24973     },
24974
24975     /**
24976      * Replaces one child node in this node with another.
24977      * @param {Node} newChild The replacement node
24978      * @param {Node} oldChild The node to replace
24979      * @return {Node} The replaced node
24980      */
24981     replaceChild : function(newChild, oldChild){
24982         this.insertBefore(newChild, oldChild);
24983         this.removeChild(oldChild);
24984         return oldChild;
24985     },
24986
24987     /**
24988      * Returns the index of a child node
24989      * @param {Node} node
24990      * @return {Number} The index of the node or -1 if it was not found
24991      */
24992     indexOf : function(child){
24993         return this.childNodes.indexOf(child);
24994     },
24995
24996     /**
24997      * Returns the tree this node is in.
24998      * @return {Tree}
24999      */
25000     getOwnerTree : function(){
25001         // if it doesn't have one, look for one
25002         if(!this.ownerTree){
25003             var p = this;
25004             while(p){
25005                 if(p.ownerTree){
25006                     this.ownerTree = p.ownerTree;
25007                     break;
25008                 }
25009                 p = p.parentNode;
25010             }
25011         }
25012         return this.ownerTree;
25013     },
25014
25015     /**
25016      * Returns depth of this node (the root node has a depth of 0)
25017      * @return {Number}
25018      */
25019     getDepth : function(){
25020         var depth = 0;
25021         var p = this;
25022         while(p.parentNode){
25023             ++depth;
25024             p = p.parentNode;
25025         }
25026         return depth;
25027     },
25028
25029     // private
25030     setOwnerTree : function(tree){
25031         // if it's move, we need to update everyone
25032         if(tree != this.ownerTree){
25033             if(this.ownerTree){
25034                 this.ownerTree.unregisterNode(this);
25035             }
25036             this.ownerTree = tree;
25037             var cs = this.childNodes;
25038             for(var i = 0, len = cs.length; i < len; i++) {
25039                 cs[i].setOwnerTree(tree);
25040             }
25041             if(tree){
25042                 tree.registerNode(this);
25043             }
25044         }
25045     },
25046
25047     /**
25048      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25049      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25050      * @return {String} The path
25051      */
25052     getPath : function(attr){
25053         attr = attr || "id";
25054         var p = this.parentNode;
25055         var b = [this.attributes[attr]];
25056         while(p){
25057             b.unshift(p.attributes[attr]);
25058             p = p.parentNode;
25059         }
25060         var sep = this.getOwnerTree().pathSeparator;
25061         return sep + b.join(sep);
25062     },
25063
25064     /**
25065      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25066      * function call will be the scope provided or the current node. The arguments to the function
25067      * will be the args provided or the current node. If the function returns false at any point,
25068      * the bubble is stopped.
25069      * @param {Function} fn The function to call
25070      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25071      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25072      */
25073     bubble : function(fn, scope, args){
25074         var p = this;
25075         while(p){
25076             if(fn.call(scope || p, args || p) === false){
25077                 break;
25078             }
25079             p = p.parentNode;
25080         }
25081     },
25082
25083     /**
25084      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25085      * function call will be the scope provided or the current node. The arguments to the function
25086      * will be the args provided or the current node. If the function returns false at any point,
25087      * the cascade is stopped on that branch.
25088      * @param {Function} fn The function to call
25089      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25090      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25091      */
25092     cascade : function(fn, scope, args){
25093         if(fn.call(scope || this, args || this) !== false){
25094             var cs = this.childNodes;
25095             for(var i = 0, len = cs.length; i < len; i++) {
25096                 cs[i].cascade(fn, scope, args);
25097             }
25098         }
25099     },
25100
25101     /**
25102      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25103      * function call will be the scope provided or the current node. The arguments to the function
25104      * will be the args provided or the current node. If the function returns false at any point,
25105      * the iteration stops.
25106      * @param {Function} fn The function to call
25107      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25108      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25109      */
25110     eachChild : function(fn, scope, args){
25111         var cs = this.childNodes;
25112         for(var i = 0, len = cs.length; i < len; i++) {
25113                 if(fn.call(scope || this, args || cs[i]) === false){
25114                     break;
25115                 }
25116         }
25117     },
25118
25119     /**
25120      * Finds the first child that has the attribute with the specified value.
25121      * @param {String} attribute The attribute name
25122      * @param {Mixed} value The value to search for
25123      * @return {Node} The found child or null if none was found
25124      */
25125     findChild : function(attribute, value){
25126         var cs = this.childNodes;
25127         for(var i = 0, len = cs.length; i < len; i++) {
25128                 if(cs[i].attributes[attribute] == value){
25129                     return cs[i];
25130                 }
25131         }
25132         return null;
25133     },
25134
25135     /**
25136      * Finds the first child by a custom function. The child matches if the function passed
25137      * returns true.
25138      * @param {Function} fn
25139      * @param {Object} scope (optional)
25140      * @return {Node} The found child or null if none was found
25141      */
25142     findChildBy : function(fn, scope){
25143         var cs = this.childNodes;
25144         for(var i = 0, len = cs.length; i < len; i++) {
25145                 if(fn.call(scope||cs[i], cs[i]) === true){
25146                     return cs[i];
25147                 }
25148         }
25149         return null;
25150     },
25151
25152     /**
25153      * Sorts this nodes children using the supplied sort function
25154      * @param {Function} fn
25155      * @param {Object} scope (optional)
25156      */
25157     sort : function(fn, scope){
25158         var cs = this.childNodes;
25159         var len = cs.length;
25160         if(len > 0){
25161             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25162             cs.sort(sortFn);
25163             for(var i = 0; i < len; i++){
25164                 var n = cs[i];
25165                 n.previousSibling = cs[i-1];
25166                 n.nextSibling = cs[i+1];
25167                 if(i == 0){
25168                     this.setFirstChild(n);
25169                 }
25170                 if(i == len-1){
25171                     this.setLastChild(n);
25172                 }
25173             }
25174         }
25175     },
25176
25177     /**
25178      * Returns true if this node is an ancestor (at any point) of the passed node.
25179      * @param {Node} node
25180      * @return {Boolean}
25181      */
25182     contains : function(node){
25183         return node.isAncestor(this);
25184     },
25185
25186     /**
25187      * Returns true if the passed node is an ancestor (at any point) of this node.
25188      * @param {Node} node
25189      * @return {Boolean}
25190      */
25191     isAncestor : function(node){
25192         var p = this.parentNode;
25193         while(p){
25194             if(p == node){
25195                 return true;
25196             }
25197             p = p.parentNode;
25198         }
25199         return false;
25200     },
25201
25202     toString : function(){
25203         return "[Node"+(this.id?" "+this.id:"")+"]";
25204     }
25205 });/*
25206  * Based on:
25207  * Ext JS Library 1.1.1
25208  * Copyright(c) 2006-2007, Ext JS, LLC.
25209  *
25210  * Originally Released Under LGPL - original licence link has changed is not relivant.
25211  *
25212  * Fork - LGPL
25213  * <script type="text/javascript">
25214  */
25215  (function(){ 
25216 /**
25217  * @class Roo.Layer
25218  * @extends Roo.Element
25219  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25220  * automatic maintaining of shadow/shim positions.
25221  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25222  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25223  * you can pass a string with a CSS class name. False turns off the shadow.
25224  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25225  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25226  * @cfg {String} cls CSS class to add to the element
25227  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25228  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25229  * @constructor
25230  * @param {Object} config An object with config options.
25231  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25232  */
25233
25234 Roo.Layer = function(config, existingEl){
25235     config = config || {};
25236     var dh = Roo.DomHelper;
25237     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25238     if(existingEl){
25239         this.dom = Roo.getDom(existingEl);
25240     }
25241     if(!this.dom){
25242         var o = config.dh || {tag: "div", cls: "x-layer"};
25243         this.dom = dh.append(pel, o);
25244     }
25245     if(config.cls){
25246         this.addClass(config.cls);
25247     }
25248     this.constrain = config.constrain !== false;
25249     this.visibilityMode = Roo.Element.VISIBILITY;
25250     if(config.id){
25251         this.id = this.dom.id = config.id;
25252     }else{
25253         this.id = Roo.id(this.dom);
25254     }
25255     this.zindex = config.zindex || this.getZIndex();
25256     this.position("absolute", this.zindex);
25257     if(config.shadow){
25258         this.shadowOffset = config.shadowOffset || 4;
25259         this.shadow = new Roo.Shadow({
25260             offset : this.shadowOffset,
25261             mode : config.shadow
25262         });
25263     }else{
25264         this.shadowOffset = 0;
25265     }
25266     this.useShim = config.shim !== false && Roo.useShims;
25267     this.useDisplay = config.useDisplay;
25268     this.hide();
25269 };
25270
25271 var supr = Roo.Element.prototype;
25272
25273 // shims are shared among layer to keep from having 100 iframes
25274 var shims = [];
25275
25276 Roo.extend(Roo.Layer, Roo.Element, {
25277
25278     getZIndex : function(){
25279         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25280     },
25281
25282     getShim : function(){
25283         if(!this.useShim){
25284             return null;
25285         }
25286         if(this.shim){
25287             return this.shim;
25288         }
25289         var shim = shims.shift();
25290         if(!shim){
25291             shim = this.createShim();
25292             shim.enableDisplayMode('block');
25293             shim.dom.style.display = 'none';
25294             shim.dom.style.visibility = 'visible';
25295         }
25296         var pn = this.dom.parentNode;
25297         if(shim.dom.parentNode != pn){
25298             pn.insertBefore(shim.dom, this.dom);
25299         }
25300         shim.setStyle('z-index', this.getZIndex()-2);
25301         this.shim = shim;
25302         return shim;
25303     },
25304
25305     hideShim : function(){
25306         if(this.shim){
25307             this.shim.setDisplayed(false);
25308             shims.push(this.shim);
25309             delete this.shim;
25310         }
25311     },
25312
25313     disableShadow : function(){
25314         if(this.shadow){
25315             this.shadowDisabled = true;
25316             this.shadow.hide();
25317             this.lastShadowOffset = this.shadowOffset;
25318             this.shadowOffset = 0;
25319         }
25320     },
25321
25322     enableShadow : function(show){
25323         if(this.shadow){
25324             this.shadowDisabled = false;
25325             this.shadowOffset = this.lastShadowOffset;
25326             delete this.lastShadowOffset;
25327             if(show){
25328                 this.sync(true);
25329             }
25330         }
25331     },
25332
25333     // private
25334     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25335     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25336     sync : function(doShow){
25337         var sw = this.shadow;
25338         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25339             var sh = this.getShim();
25340
25341             var w = this.getWidth(),
25342                 h = this.getHeight();
25343
25344             var l = this.getLeft(true),
25345                 t = this.getTop(true);
25346
25347             if(sw && !this.shadowDisabled){
25348                 if(doShow && !sw.isVisible()){
25349                     sw.show(this);
25350                 }else{
25351                     sw.realign(l, t, w, h);
25352                 }
25353                 if(sh){
25354                     if(doShow){
25355                        sh.show();
25356                     }
25357                     // fit the shim behind the shadow, so it is shimmed too
25358                     var a = sw.adjusts, s = sh.dom.style;
25359                     s.left = (Math.min(l, l+a.l))+"px";
25360                     s.top = (Math.min(t, t+a.t))+"px";
25361                     s.width = (w+a.w)+"px";
25362                     s.height = (h+a.h)+"px";
25363                 }
25364             }else if(sh){
25365                 if(doShow){
25366                    sh.show();
25367                 }
25368                 sh.setSize(w, h);
25369                 sh.setLeftTop(l, t);
25370             }
25371             
25372         }
25373     },
25374
25375     // private
25376     destroy : function(){
25377         this.hideShim();
25378         if(this.shadow){
25379             this.shadow.hide();
25380         }
25381         this.removeAllListeners();
25382         var pn = this.dom.parentNode;
25383         if(pn){
25384             pn.removeChild(this.dom);
25385         }
25386         Roo.Element.uncache(this.id);
25387     },
25388
25389     remove : function(){
25390         this.destroy();
25391     },
25392
25393     // private
25394     beginUpdate : function(){
25395         this.updating = true;
25396     },
25397
25398     // private
25399     endUpdate : function(){
25400         this.updating = false;
25401         this.sync(true);
25402     },
25403
25404     // private
25405     hideUnders : function(negOffset){
25406         if(this.shadow){
25407             this.shadow.hide();
25408         }
25409         this.hideShim();
25410     },
25411
25412     // private
25413     constrainXY : function(){
25414         if(this.constrain){
25415             var vw = Roo.lib.Dom.getViewWidth(),
25416                 vh = Roo.lib.Dom.getViewHeight();
25417             var s = Roo.get(document).getScroll();
25418
25419             var xy = this.getXY();
25420             var x = xy[0], y = xy[1];   
25421             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25422             // only move it if it needs it
25423             var moved = false;
25424             // first validate right/bottom
25425             if((x + w) > vw+s.left){
25426                 x = vw - w - this.shadowOffset;
25427                 moved = true;
25428             }
25429             if((y + h) > vh+s.top){
25430                 y = vh - h - this.shadowOffset;
25431                 moved = true;
25432             }
25433             // then make sure top/left isn't negative
25434             if(x < s.left){
25435                 x = s.left;
25436                 moved = true;
25437             }
25438             if(y < s.top){
25439                 y = s.top;
25440                 moved = true;
25441             }
25442             if(moved){
25443                 if(this.avoidY){
25444                     var ay = this.avoidY;
25445                     if(y <= ay && (y+h) >= ay){
25446                         y = ay-h-5;   
25447                     }
25448                 }
25449                 xy = [x, y];
25450                 this.storeXY(xy);
25451                 supr.setXY.call(this, xy);
25452                 this.sync();
25453             }
25454         }
25455     },
25456
25457     isVisible : function(){
25458         return this.visible;    
25459     },
25460
25461     // private
25462     showAction : function(){
25463         this.visible = true; // track visibility to prevent getStyle calls
25464         if(this.useDisplay === true){
25465             this.setDisplayed("");
25466         }else if(this.lastXY){
25467             supr.setXY.call(this, this.lastXY);
25468         }else if(this.lastLT){
25469             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25470         }
25471     },
25472
25473     // private
25474     hideAction : function(){
25475         this.visible = false;
25476         if(this.useDisplay === true){
25477             this.setDisplayed(false);
25478         }else{
25479             this.setLeftTop(-10000,-10000);
25480         }
25481     },
25482
25483     // overridden Element method
25484     setVisible : function(v, a, d, c, e){
25485         if(v){
25486             this.showAction();
25487         }
25488         if(a && v){
25489             var cb = function(){
25490                 this.sync(true);
25491                 if(c){
25492                     c();
25493                 }
25494             }.createDelegate(this);
25495             supr.setVisible.call(this, true, true, d, cb, e);
25496         }else{
25497             if(!v){
25498                 this.hideUnders(true);
25499             }
25500             var cb = c;
25501             if(a){
25502                 cb = function(){
25503                     this.hideAction();
25504                     if(c){
25505                         c();
25506                     }
25507                 }.createDelegate(this);
25508             }
25509             supr.setVisible.call(this, v, a, d, cb, e);
25510             if(v){
25511                 this.sync(true);
25512             }else if(!a){
25513                 this.hideAction();
25514             }
25515         }
25516     },
25517
25518     storeXY : function(xy){
25519         delete this.lastLT;
25520         this.lastXY = xy;
25521     },
25522
25523     storeLeftTop : function(left, top){
25524         delete this.lastXY;
25525         this.lastLT = [left, top];
25526     },
25527
25528     // private
25529     beforeFx : function(){
25530         this.beforeAction();
25531         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25532     },
25533
25534     // private
25535     afterFx : function(){
25536         Roo.Layer.superclass.afterFx.apply(this, arguments);
25537         this.sync(this.isVisible());
25538     },
25539
25540     // private
25541     beforeAction : function(){
25542         if(!this.updating && this.shadow){
25543             this.shadow.hide();
25544         }
25545     },
25546
25547     // overridden Element method
25548     setLeft : function(left){
25549         this.storeLeftTop(left, this.getTop(true));
25550         supr.setLeft.apply(this, arguments);
25551         this.sync();
25552     },
25553
25554     setTop : function(top){
25555         this.storeLeftTop(this.getLeft(true), top);
25556         supr.setTop.apply(this, arguments);
25557         this.sync();
25558     },
25559
25560     setLeftTop : function(left, top){
25561         this.storeLeftTop(left, top);
25562         supr.setLeftTop.apply(this, arguments);
25563         this.sync();
25564     },
25565
25566     setXY : function(xy, a, d, c, e){
25567         this.fixDisplay();
25568         this.beforeAction();
25569         this.storeXY(xy);
25570         var cb = this.createCB(c);
25571         supr.setXY.call(this, xy, a, d, cb, e);
25572         if(!a){
25573             cb();
25574         }
25575     },
25576
25577     // private
25578     createCB : function(c){
25579         var el = this;
25580         return function(){
25581             el.constrainXY();
25582             el.sync(true);
25583             if(c){
25584                 c();
25585             }
25586         };
25587     },
25588
25589     // overridden Element method
25590     setX : function(x, a, d, c, e){
25591         this.setXY([x, this.getY()], a, d, c, e);
25592     },
25593
25594     // overridden Element method
25595     setY : function(y, a, d, c, e){
25596         this.setXY([this.getX(), y], a, d, c, e);
25597     },
25598
25599     // overridden Element method
25600     setSize : function(w, h, a, d, c, e){
25601         this.beforeAction();
25602         var cb = this.createCB(c);
25603         supr.setSize.call(this, w, h, a, d, cb, e);
25604         if(!a){
25605             cb();
25606         }
25607     },
25608
25609     // overridden Element method
25610     setWidth : function(w, a, d, c, e){
25611         this.beforeAction();
25612         var cb = this.createCB(c);
25613         supr.setWidth.call(this, w, a, d, cb, e);
25614         if(!a){
25615             cb();
25616         }
25617     },
25618
25619     // overridden Element method
25620     setHeight : function(h, a, d, c, e){
25621         this.beforeAction();
25622         var cb = this.createCB(c);
25623         supr.setHeight.call(this, h, a, d, cb, e);
25624         if(!a){
25625             cb();
25626         }
25627     },
25628
25629     // overridden Element method
25630     setBounds : function(x, y, w, h, a, d, c, e){
25631         this.beforeAction();
25632         var cb = this.createCB(c);
25633         if(!a){
25634             this.storeXY([x, y]);
25635             supr.setXY.call(this, [x, y]);
25636             supr.setSize.call(this, w, h, a, d, cb, e);
25637             cb();
25638         }else{
25639             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25640         }
25641         return this;
25642     },
25643     
25644     /**
25645      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25646      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25647      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25648      * @param {Number} zindex The new z-index to set
25649      * @return {this} The Layer
25650      */
25651     setZIndex : function(zindex){
25652         this.zindex = zindex;
25653         this.setStyle("z-index", zindex + 2);
25654         if(this.shadow){
25655             this.shadow.setZIndex(zindex + 1);
25656         }
25657         if(this.shim){
25658             this.shim.setStyle("z-index", zindex);
25659         }
25660     }
25661 });
25662 })();/*
25663  * Based on:
25664  * Ext JS Library 1.1.1
25665  * Copyright(c) 2006-2007, Ext JS, LLC.
25666  *
25667  * Originally Released Under LGPL - original licence link has changed is not relivant.
25668  *
25669  * Fork - LGPL
25670  * <script type="text/javascript">
25671  */
25672
25673
25674 /**
25675  * @class Roo.Shadow
25676  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25677  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25678  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25679  * @constructor
25680  * Create a new Shadow
25681  * @param {Object} config The config object
25682  */
25683 Roo.Shadow = function(config){
25684     Roo.apply(this, config);
25685     if(typeof this.mode != "string"){
25686         this.mode = this.defaultMode;
25687     }
25688     var o = this.offset, a = {h: 0};
25689     var rad = Math.floor(this.offset/2);
25690     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25691         case "drop":
25692             a.w = 0;
25693             a.l = a.t = o;
25694             a.t -= 1;
25695             if(Roo.isIE){
25696                 a.l -= this.offset + rad;
25697                 a.t -= this.offset + rad;
25698                 a.w -= rad;
25699                 a.h -= rad;
25700                 a.t += 1;
25701             }
25702         break;
25703         case "sides":
25704             a.w = (o*2);
25705             a.l = -o;
25706             a.t = o-1;
25707             if(Roo.isIE){
25708                 a.l -= (this.offset - rad);
25709                 a.t -= this.offset + rad;
25710                 a.l += 1;
25711                 a.w -= (this.offset - rad)*2;
25712                 a.w -= rad + 1;
25713                 a.h -= 1;
25714             }
25715         break;
25716         case "frame":
25717             a.w = a.h = (o*2);
25718             a.l = a.t = -o;
25719             a.t += 1;
25720             a.h -= 2;
25721             if(Roo.isIE){
25722                 a.l -= (this.offset - rad);
25723                 a.t -= (this.offset - rad);
25724                 a.l += 1;
25725                 a.w -= (this.offset + rad + 1);
25726                 a.h -= (this.offset + rad);
25727                 a.h += 1;
25728             }
25729         break;
25730     };
25731
25732     this.adjusts = a;
25733 };
25734
25735 Roo.Shadow.prototype = {
25736     /**
25737      * @cfg {String} mode
25738      * The shadow display mode.  Supports the following options:<br />
25739      * sides: Shadow displays on both sides and bottom only<br />
25740      * frame: Shadow displays equally on all four sides<br />
25741      * drop: Traditional bottom-right drop shadow (default)
25742      */
25743     /**
25744      * @cfg {String} offset
25745      * The number of pixels to offset the shadow from the element (defaults to 4)
25746      */
25747     offset: 4,
25748
25749     // private
25750     defaultMode: "drop",
25751
25752     /**
25753      * Displays the shadow under the target element
25754      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25755      */
25756     show : function(target){
25757         target = Roo.get(target);
25758         if(!this.el){
25759             this.el = Roo.Shadow.Pool.pull();
25760             if(this.el.dom.nextSibling != target.dom){
25761                 this.el.insertBefore(target);
25762             }
25763         }
25764         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25765         if(Roo.isIE){
25766             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25767         }
25768         this.realign(
25769             target.getLeft(true),
25770             target.getTop(true),
25771             target.getWidth(),
25772             target.getHeight()
25773         );
25774         this.el.dom.style.display = "block";
25775     },
25776
25777     /**
25778      * Returns true if the shadow is visible, else false
25779      */
25780     isVisible : function(){
25781         return this.el ? true : false;  
25782     },
25783
25784     /**
25785      * Direct alignment when values are already available. Show must be called at least once before
25786      * calling this method to ensure it is initialized.
25787      * @param {Number} left The target element left position
25788      * @param {Number} top The target element top position
25789      * @param {Number} width The target element width
25790      * @param {Number} height The target element height
25791      */
25792     realign : function(l, t, w, h){
25793         if(!this.el){
25794             return;
25795         }
25796         var a = this.adjusts, d = this.el.dom, s = d.style;
25797         var iea = 0;
25798         s.left = (l+a.l)+"px";
25799         s.top = (t+a.t)+"px";
25800         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25801  
25802         if(s.width != sws || s.height != shs){
25803             s.width = sws;
25804             s.height = shs;
25805             if(!Roo.isIE){
25806                 var cn = d.childNodes;
25807                 var sww = Math.max(0, (sw-12))+"px";
25808                 cn[0].childNodes[1].style.width = sww;
25809                 cn[1].childNodes[1].style.width = sww;
25810                 cn[2].childNodes[1].style.width = sww;
25811                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25812             }
25813         }
25814     },
25815
25816     /**
25817      * Hides this shadow
25818      */
25819     hide : function(){
25820         if(this.el){
25821             this.el.dom.style.display = "none";
25822             Roo.Shadow.Pool.push(this.el);
25823             delete this.el;
25824         }
25825     },
25826
25827     /**
25828      * Adjust the z-index of this shadow
25829      * @param {Number} zindex The new z-index
25830      */
25831     setZIndex : function(z){
25832         this.zIndex = z;
25833         if(this.el){
25834             this.el.setStyle("z-index", z);
25835         }
25836     }
25837 };
25838
25839 // Private utility class that manages the internal Shadow cache
25840 Roo.Shadow.Pool = function(){
25841     var p = [];
25842     var markup = Roo.isIE ?
25843                  '<div class="x-ie-shadow"></div>' :
25844                  '<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>';
25845     return {
25846         pull : function(){
25847             var sh = p.shift();
25848             if(!sh){
25849                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25850                 sh.autoBoxAdjust = false;
25851             }
25852             return sh;
25853         },
25854
25855         push : function(sh){
25856             p.push(sh);
25857         }
25858     };
25859 }();/*
25860  * Based on:
25861  * Ext JS Library 1.1.1
25862  * Copyright(c) 2006-2007, Ext JS, LLC.
25863  *
25864  * Originally Released Under LGPL - original licence link has changed is not relivant.
25865  *
25866  * Fork - LGPL
25867  * <script type="text/javascript">
25868  */
25869
25870
25871 /**
25872  * @class Roo.SplitBar
25873  * @extends Roo.util.Observable
25874  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25875  * <br><br>
25876  * Usage:
25877  * <pre><code>
25878 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25879                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25880 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25881 split.minSize = 100;
25882 split.maxSize = 600;
25883 split.animate = true;
25884 split.on('moved', splitterMoved);
25885 </code></pre>
25886  * @constructor
25887  * Create a new SplitBar
25888  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25889  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25890  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25891  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25892                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25893                         position of the SplitBar).
25894  */
25895 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25896     
25897     /** @private */
25898     this.el = Roo.get(dragElement, true);
25899     this.el.dom.unselectable = "on";
25900     /** @private */
25901     this.resizingEl = Roo.get(resizingElement, true);
25902
25903     /**
25904      * @private
25905      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25906      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25907      * @type Number
25908      */
25909     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25910     
25911     /**
25912      * The minimum size of the resizing element. (Defaults to 0)
25913      * @type Number
25914      */
25915     this.minSize = 0;
25916     
25917     /**
25918      * The maximum size of the resizing element. (Defaults to 2000)
25919      * @type Number
25920      */
25921     this.maxSize = 2000;
25922     
25923     /**
25924      * Whether to animate the transition to the new size
25925      * @type Boolean
25926      */
25927     this.animate = false;
25928     
25929     /**
25930      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25931      * @type Boolean
25932      */
25933     this.useShim = false;
25934     
25935     /** @private */
25936     this.shim = null;
25937     
25938     if(!existingProxy){
25939         /** @private */
25940         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25941     }else{
25942         this.proxy = Roo.get(existingProxy).dom;
25943     }
25944     /** @private */
25945     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25946     
25947     /** @private */
25948     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25949     
25950     /** @private */
25951     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25952     
25953     /** @private */
25954     this.dragSpecs = {};
25955     
25956     /**
25957      * @private The adapter to use to positon and resize elements
25958      */
25959     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
25960     this.adapter.init(this);
25961     
25962     if(this.orientation == Roo.SplitBar.HORIZONTAL){
25963         /** @private */
25964         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
25965         this.el.addClass("x-splitbar-h");
25966     }else{
25967         /** @private */
25968         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
25969         this.el.addClass("x-splitbar-v");
25970     }
25971     
25972     this.addEvents({
25973         /**
25974          * @event resize
25975          * Fires when the splitter is moved (alias for {@link #event-moved})
25976          * @param {Roo.SplitBar} this
25977          * @param {Number} newSize the new width or height
25978          */
25979         "resize" : true,
25980         /**
25981          * @event moved
25982          * Fires when the splitter is moved
25983          * @param {Roo.SplitBar} this
25984          * @param {Number} newSize the new width or height
25985          */
25986         "moved" : true,
25987         /**
25988          * @event beforeresize
25989          * Fires before the splitter is dragged
25990          * @param {Roo.SplitBar} this
25991          */
25992         "beforeresize" : true,
25993
25994         "beforeapply" : true
25995     });
25996
25997     Roo.util.Observable.call(this);
25998 };
25999
26000 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26001     onStartProxyDrag : function(x, y){
26002         this.fireEvent("beforeresize", this);
26003         if(!this.overlay){
26004             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26005             o.unselectable();
26006             o.enableDisplayMode("block");
26007             // all splitbars share the same overlay
26008             Roo.SplitBar.prototype.overlay = o;
26009         }
26010         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26011         this.overlay.show();
26012         Roo.get(this.proxy).setDisplayed("block");
26013         var size = this.adapter.getElementSize(this);
26014         this.activeMinSize = this.getMinimumSize();;
26015         this.activeMaxSize = this.getMaximumSize();;
26016         var c1 = size - this.activeMinSize;
26017         var c2 = Math.max(this.activeMaxSize - size, 0);
26018         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26019             this.dd.resetConstraints();
26020             this.dd.setXConstraint(
26021                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26022                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26023             );
26024             this.dd.setYConstraint(0, 0);
26025         }else{
26026             this.dd.resetConstraints();
26027             this.dd.setXConstraint(0, 0);
26028             this.dd.setYConstraint(
26029                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26030                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26031             );
26032          }
26033         this.dragSpecs.startSize = size;
26034         this.dragSpecs.startPoint = [x, y];
26035         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26036     },
26037     
26038     /** 
26039      * @private Called after the drag operation by the DDProxy
26040      */
26041     onEndProxyDrag : function(e){
26042         Roo.get(this.proxy).setDisplayed(false);
26043         var endPoint = Roo.lib.Event.getXY(e);
26044         if(this.overlay){
26045             this.overlay.hide();
26046         }
26047         var newSize;
26048         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26049             newSize = this.dragSpecs.startSize + 
26050                 (this.placement == Roo.SplitBar.LEFT ?
26051                     endPoint[0] - this.dragSpecs.startPoint[0] :
26052                     this.dragSpecs.startPoint[0] - endPoint[0]
26053                 );
26054         }else{
26055             newSize = this.dragSpecs.startSize + 
26056                 (this.placement == Roo.SplitBar.TOP ?
26057                     endPoint[1] - this.dragSpecs.startPoint[1] :
26058                     this.dragSpecs.startPoint[1] - endPoint[1]
26059                 );
26060         }
26061         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26062         if(newSize != this.dragSpecs.startSize){
26063             if(this.fireEvent('beforeapply', this, newSize) !== false){
26064                 this.adapter.setElementSize(this, newSize);
26065                 this.fireEvent("moved", this, newSize);
26066                 this.fireEvent("resize", this, newSize);
26067             }
26068         }
26069     },
26070     
26071     /**
26072      * Get the adapter this SplitBar uses
26073      * @return The adapter object
26074      */
26075     getAdapter : function(){
26076         return this.adapter;
26077     },
26078     
26079     /**
26080      * Set the adapter this SplitBar uses
26081      * @param {Object} adapter A SplitBar adapter object
26082      */
26083     setAdapter : function(adapter){
26084         this.adapter = adapter;
26085         this.adapter.init(this);
26086     },
26087     
26088     /**
26089      * Gets the minimum size for the resizing element
26090      * @return {Number} The minimum size
26091      */
26092     getMinimumSize : function(){
26093         return this.minSize;
26094     },
26095     
26096     /**
26097      * Sets the minimum size for the resizing element
26098      * @param {Number} minSize The minimum size
26099      */
26100     setMinimumSize : function(minSize){
26101         this.minSize = minSize;
26102     },
26103     
26104     /**
26105      * Gets the maximum size for the resizing element
26106      * @return {Number} The maximum size
26107      */
26108     getMaximumSize : function(){
26109         return this.maxSize;
26110     },
26111     
26112     /**
26113      * Sets the maximum size for the resizing element
26114      * @param {Number} maxSize The maximum size
26115      */
26116     setMaximumSize : function(maxSize){
26117         this.maxSize = maxSize;
26118     },
26119     
26120     /**
26121      * Sets the initialize size for the resizing element
26122      * @param {Number} size The initial size
26123      */
26124     setCurrentSize : function(size){
26125         var oldAnimate = this.animate;
26126         this.animate = false;
26127         this.adapter.setElementSize(this, size);
26128         this.animate = oldAnimate;
26129     },
26130     
26131     /**
26132      * Destroy this splitbar. 
26133      * @param {Boolean} removeEl True to remove the element
26134      */
26135     destroy : function(removeEl){
26136         if(this.shim){
26137             this.shim.remove();
26138         }
26139         this.dd.unreg();
26140         this.proxy.parentNode.removeChild(this.proxy);
26141         if(removeEl){
26142             this.el.remove();
26143         }
26144     }
26145 });
26146
26147 /**
26148  * @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.
26149  */
26150 Roo.SplitBar.createProxy = function(dir){
26151     var proxy = new Roo.Element(document.createElement("div"));
26152     proxy.unselectable();
26153     var cls = 'x-splitbar-proxy';
26154     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26155     document.body.appendChild(proxy.dom);
26156     return proxy.dom;
26157 };
26158
26159 /** 
26160  * @class Roo.SplitBar.BasicLayoutAdapter
26161  * Default Adapter. It assumes the splitter and resizing element are not positioned
26162  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26163  */
26164 Roo.SplitBar.BasicLayoutAdapter = function(){
26165 };
26166
26167 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26168     // do nothing for now
26169     init : function(s){
26170     
26171     },
26172     /**
26173      * Called before drag operations to get the current size of the resizing element. 
26174      * @param {Roo.SplitBar} s The SplitBar using this adapter
26175      */
26176      getElementSize : function(s){
26177         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26178             return s.resizingEl.getWidth();
26179         }else{
26180             return s.resizingEl.getHeight();
26181         }
26182     },
26183     
26184     /**
26185      * Called after drag operations to set the size of the resizing element.
26186      * @param {Roo.SplitBar} s The SplitBar using this adapter
26187      * @param {Number} newSize The new size to set
26188      * @param {Function} onComplete A function to be invoked when resizing is complete
26189      */
26190     setElementSize : function(s, newSize, onComplete){
26191         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26192             if(!s.animate){
26193                 s.resizingEl.setWidth(newSize);
26194                 if(onComplete){
26195                     onComplete(s, newSize);
26196                 }
26197             }else{
26198                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26199             }
26200         }else{
26201             
26202             if(!s.animate){
26203                 s.resizingEl.setHeight(newSize);
26204                 if(onComplete){
26205                     onComplete(s, newSize);
26206                 }
26207             }else{
26208                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26209             }
26210         }
26211     }
26212 };
26213
26214 /** 
26215  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26216  * @extends Roo.SplitBar.BasicLayoutAdapter
26217  * Adapter that  moves the splitter element to align with the resized sizing element. 
26218  * Used with an absolute positioned SplitBar.
26219  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26220  * document.body, make sure you assign an id to the body element.
26221  */
26222 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26223     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26224     this.container = Roo.get(container);
26225 };
26226
26227 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26228     init : function(s){
26229         this.basic.init(s);
26230     },
26231     
26232     getElementSize : function(s){
26233         return this.basic.getElementSize(s);
26234     },
26235     
26236     setElementSize : function(s, newSize, onComplete){
26237         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26238     },
26239     
26240     moveSplitter : function(s){
26241         var yes = Roo.SplitBar;
26242         switch(s.placement){
26243             case yes.LEFT:
26244                 s.el.setX(s.resizingEl.getRight());
26245                 break;
26246             case yes.RIGHT:
26247                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26248                 break;
26249             case yes.TOP:
26250                 s.el.setY(s.resizingEl.getBottom());
26251                 break;
26252             case yes.BOTTOM:
26253                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26254                 break;
26255         }
26256     }
26257 };
26258
26259 /**
26260  * Orientation constant - Create a vertical SplitBar
26261  * @static
26262  * @type Number
26263  */
26264 Roo.SplitBar.VERTICAL = 1;
26265
26266 /**
26267  * Orientation constant - Create a horizontal SplitBar
26268  * @static
26269  * @type Number
26270  */
26271 Roo.SplitBar.HORIZONTAL = 2;
26272
26273 /**
26274  * Placement constant - The resizing element is to the left of the splitter element
26275  * @static
26276  * @type Number
26277  */
26278 Roo.SplitBar.LEFT = 1;
26279
26280 /**
26281  * Placement constant - The resizing element is to the right of the splitter element
26282  * @static
26283  * @type Number
26284  */
26285 Roo.SplitBar.RIGHT = 2;
26286
26287 /**
26288  * Placement constant - The resizing element is positioned above the splitter element
26289  * @static
26290  * @type Number
26291  */
26292 Roo.SplitBar.TOP = 3;
26293
26294 /**
26295  * Placement constant - The resizing element is positioned under splitter element
26296  * @static
26297  * @type Number
26298  */
26299 Roo.SplitBar.BOTTOM = 4;
26300 /*
26301  * Based on:
26302  * Ext JS Library 1.1.1
26303  * Copyright(c) 2006-2007, Ext JS, LLC.
26304  *
26305  * Originally Released Under LGPL - original licence link has changed is not relivant.
26306  *
26307  * Fork - LGPL
26308  * <script type="text/javascript">
26309  */
26310
26311 /**
26312  * @class Roo.View
26313  * @extends Roo.util.Observable
26314  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26315  * This class also supports single and multi selection modes. <br>
26316  * Create a data model bound view:
26317  <pre><code>
26318  var store = new Roo.data.Store(...);
26319
26320  var view = new Roo.View({
26321     el : "my-element",
26322     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26323  
26324     singleSelect: true,
26325     selectedClass: "ydataview-selected",
26326     store: store
26327  });
26328
26329  // listen for node click?
26330  view.on("click", function(vw, index, node, e){
26331  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26332  });
26333
26334  // load XML data
26335  dataModel.load("foobar.xml");
26336  </code></pre>
26337  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26338  * <br><br>
26339  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26340  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26341  * 
26342  * Note: old style constructor is still suported (container, template, config)
26343  * 
26344  * @constructor
26345  * Create a new View
26346  * @param {Object} config The config object
26347  * 
26348  */
26349 Roo.View = function(config, depreciated_tpl, depreciated_config){
26350     
26351     this.parent = false;
26352     
26353     if (typeof(depreciated_tpl) == 'undefined') {
26354         // new way.. - universal constructor.
26355         Roo.apply(this, config);
26356         this.el  = Roo.get(this.el);
26357     } else {
26358         // old format..
26359         this.el  = Roo.get(config);
26360         this.tpl = depreciated_tpl;
26361         Roo.apply(this, depreciated_config);
26362     }
26363     this.wrapEl  = this.el.wrap().wrap();
26364     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26365     
26366     
26367     if(typeof(this.tpl) == "string"){
26368         this.tpl = new Roo.Template(this.tpl);
26369     } else {
26370         // support xtype ctors..
26371         this.tpl = new Roo.factory(this.tpl, Roo);
26372     }
26373     
26374     
26375     this.tpl.compile();
26376     
26377     /** @private */
26378     this.addEvents({
26379         /**
26380          * @event beforeclick
26381          * Fires before a click is processed. Returns false to cancel the default action.
26382          * @param {Roo.View} this
26383          * @param {Number} index The index of the target node
26384          * @param {HTMLElement} node The target node
26385          * @param {Roo.EventObject} e The raw event object
26386          */
26387             "beforeclick" : true,
26388         /**
26389          * @event click
26390          * Fires when a template node is clicked.
26391          * @param {Roo.View} this
26392          * @param {Number} index The index of the target node
26393          * @param {HTMLElement} node The target node
26394          * @param {Roo.EventObject} e The raw event object
26395          */
26396             "click" : true,
26397         /**
26398          * @event dblclick
26399          * Fires when a template node is double clicked.
26400          * @param {Roo.View} this
26401          * @param {Number} index The index of the target node
26402          * @param {HTMLElement} node The target node
26403          * @param {Roo.EventObject} e The raw event object
26404          */
26405             "dblclick" : true,
26406         /**
26407          * @event contextmenu
26408          * Fires when a template node is right clicked.
26409          * @param {Roo.View} this
26410          * @param {Number} index The index of the target node
26411          * @param {HTMLElement} node The target node
26412          * @param {Roo.EventObject} e The raw event object
26413          */
26414             "contextmenu" : true,
26415         /**
26416          * @event selectionchange
26417          * Fires when the selected nodes change.
26418          * @param {Roo.View} this
26419          * @param {Array} selections Array of the selected nodes
26420          */
26421             "selectionchange" : true,
26422     
26423         /**
26424          * @event beforeselect
26425          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26426          * @param {Roo.View} this
26427          * @param {HTMLElement} node The node to be selected
26428          * @param {Array} selections Array of currently selected nodes
26429          */
26430             "beforeselect" : true,
26431         /**
26432          * @event preparedata
26433          * Fires on every row to render, to allow you to change the data.
26434          * @param {Roo.View} this
26435          * @param {Object} data to be rendered (change this)
26436          */
26437           "preparedata" : true
26438           
26439           
26440         });
26441
26442
26443
26444     this.el.on({
26445         "click": this.onClick,
26446         "dblclick": this.onDblClick,
26447         "contextmenu": this.onContextMenu,
26448         scope:this
26449     });
26450
26451     this.selections = [];
26452     this.nodes = [];
26453     this.cmp = new Roo.CompositeElementLite([]);
26454     if(this.store){
26455         this.store = Roo.factory(this.store, Roo.data);
26456         this.setStore(this.store, true);
26457     }
26458     
26459     if ( this.footer && this.footer.xtype) {
26460            
26461          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26462         
26463         this.footer.dataSource = this.store;
26464         this.footer.container = fctr;
26465         this.footer = Roo.factory(this.footer, Roo);
26466         fctr.insertFirst(this.el);
26467         
26468         // this is a bit insane - as the paging toolbar seems to detach the el..
26469 //        dom.parentNode.parentNode.parentNode
26470          // they get detached?
26471     }
26472     
26473     
26474     Roo.View.superclass.constructor.call(this);
26475     
26476     
26477 };
26478
26479 Roo.extend(Roo.View, Roo.util.Observable, {
26480     
26481      /**
26482      * @cfg {Roo.data.Store} store Data store to load data from.
26483      */
26484     store : false,
26485     
26486     /**
26487      * @cfg {String|Roo.Element} el The container element.
26488      */
26489     el : '',
26490     
26491     /**
26492      * @cfg {String|Roo.Template} tpl The template used by this View 
26493      */
26494     tpl : false,
26495     /**
26496      * @cfg {String} dataName the named area of the template to use as the data area
26497      *                          Works with domtemplates roo-name="name"
26498      */
26499     dataName: false,
26500     /**
26501      * @cfg {String} selectedClass The css class to add to selected nodes
26502      */
26503     selectedClass : "x-view-selected",
26504      /**
26505      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26506      */
26507     emptyText : "",
26508     
26509     /**
26510      * @cfg {String} text to display on mask (default Loading)
26511      */
26512     mask : false,
26513     /**
26514      * @cfg {Boolean} multiSelect Allow multiple selection
26515      */
26516     multiSelect : false,
26517     /**
26518      * @cfg {Boolean} singleSelect Allow single selection
26519      */
26520     singleSelect:  false,
26521     
26522     /**
26523      * @cfg {Boolean} toggleSelect - selecting 
26524      */
26525     toggleSelect : false,
26526     
26527     /**
26528      * @cfg {Boolean} tickable - selecting 
26529      */
26530     tickable : false,
26531     
26532     /**
26533      * Returns the element this view is bound to.
26534      * @return {Roo.Element}
26535      */
26536     getEl : function(){
26537         return this.wrapEl;
26538     },
26539     
26540     
26541
26542     /**
26543      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26544      */
26545     refresh : function(){
26546         //Roo.log('refresh');
26547         var t = this.tpl;
26548         
26549         // if we are using something like 'domtemplate', then
26550         // the what gets used is:
26551         // t.applySubtemplate(NAME, data, wrapping data..)
26552         // the outer template then get' applied with
26553         //     the store 'extra data'
26554         // and the body get's added to the
26555         //      roo-name="data" node?
26556         //      <span class='roo-tpl-{name}'></span> ?????
26557         
26558         
26559         
26560         this.clearSelections();
26561         this.el.update("");
26562         var html = [];
26563         var records = this.store.getRange();
26564         if(records.length < 1) {
26565             
26566             // is this valid??  = should it render a template??
26567             
26568             this.el.update(this.emptyText);
26569             return;
26570         }
26571         var el = this.el;
26572         if (this.dataName) {
26573             this.el.update(t.apply(this.store.meta)); //????
26574             el = this.el.child('.roo-tpl-' + this.dataName);
26575         }
26576         
26577         for(var i = 0, len = records.length; i < len; i++){
26578             var data = this.prepareData(records[i].data, i, records[i]);
26579             this.fireEvent("preparedata", this, data, i, records[i]);
26580             
26581             var d = Roo.apply({}, data);
26582             
26583             if(this.tickable){
26584                 Roo.apply(d, {'roo-id' : Roo.id()});
26585                 
26586                 var _this = this;
26587             
26588                 Roo.each(this.parent.item, function(item){
26589                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26590                         return;
26591                     }
26592                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26593                 });
26594             }
26595             
26596             html[html.length] = Roo.util.Format.trim(
26597                 this.dataName ?
26598                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26599                     t.apply(d)
26600             );
26601         }
26602         
26603         
26604         
26605         el.update(html.join(""));
26606         this.nodes = el.dom.childNodes;
26607         this.updateIndexes(0);
26608     },
26609     
26610
26611     /**
26612      * Function to override to reformat the data that is sent to
26613      * the template for each node.
26614      * DEPRICATED - use the preparedata event handler.
26615      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26616      * a JSON object for an UpdateManager bound view).
26617      */
26618     prepareData : function(data, index, record)
26619     {
26620         this.fireEvent("preparedata", this, data, index, record);
26621         return data;
26622     },
26623
26624     onUpdate : function(ds, record){
26625         // Roo.log('on update');   
26626         this.clearSelections();
26627         var index = this.store.indexOf(record);
26628         var n = this.nodes[index];
26629         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26630         n.parentNode.removeChild(n);
26631         this.updateIndexes(index, index);
26632     },
26633
26634     
26635     
26636 // --------- FIXME     
26637     onAdd : function(ds, records, index)
26638     {
26639         //Roo.log(['on Add', ds, records, index] );        
26640         this.clearSelections();
26641         if(this.nodes.length == 0){
26642             this.refresh();
26643             return;
26644         }
26645         var n = this.nodes[index];
26646         for(var i = 0, len = records.length; i < len; i++){
26647             var d = this.prepareData(records[i].data, i, records[i]);
26648             if(n){
26649                 this.tpl.insertBefore(n, d);
26650             }else{
26651                 
26652                 this.tpl.append(this.el, d);
26653             }
26654         }
26655         this.updateIndexes(index);
26656     },
26657
26658     onRemove : function(ds, record, index){
26659        // Roo.log('onRemove');
26660         this.clearSelections();
26661         var el = this.dataName  ?
26662             this.el.child('.roo-tpl-' + this.dataName) :
26663             this.el; 
26664         
26665         el.dom.removeChild(this.nodes[index]);
26666         this.updateIndexes(index);
26667     },
26668
26669     /**
26670      * Refresh an individual node.
26671      * @param {Number} index
26672      */
26673     refreshNode : function(index){
26674         this.onUpdate(this.store, this.store.getAt(index));
26675     },
26676
26677     updateIndexes : function(startIndex, endIndex){
26678         var ns = this.nodes;
26679         startIndex = startIndex || 0;
26680         endIndex = endIndex || ns.length - 1;
26681         for(var i = startIndex; i <= endIndex; i++){
26682             ns[i].nodeIndex = i;
26683         }
26684     },
26685
26686     /**
26687      * Changes the data store this view uses and refresh the view.
26688      * @param {Store} store
26689      */
26690     setStore : function(store, initial){
26691         if(!initial && this.store){
26692             this.store.un("datachanged", this.refresh);
26693             this.store.un("add", this.onAdd);
26694             this.store.un("remove", this.onRemove);
26695             this.store.un("update", this.onUpdate);
26696             this.store.un("clear", this.refresh);
26697             this.store.un("beforeload", this.onBeforeLoad);
26698             this.store.un("load", this.onLoad);
26699             this.store.un("loadexception", this.onLoad);
26700         }
26701         if(store){
26702           
26703             store.on("datachanged", this.refresh, this);
26704             store.on("add", this.onAdd, this);
26705             store.on("remove", this.onRemove, this);
26706             store.on("update", this.onUpdate, this);
26707             store.on("clear", this.refresh, this);
26708             store.on("beforeload", this.onBeforeLoad, this);
26709             store.on("load", this.onLoad, this);
26710             store.on("loadexception", this.onLoad, this);
26711         }
26712         
26713         if(store){
26714             this.refresh();
26715         }
26716     },
26717     /**
26718      * onbeforeLoad - masks the loading area.
26719      *
26720      */
26721     onBeforeLoad : function(store,opts)
26722     {
26723          //Roo.log('onBeforeLoad');   
26724         if (!opts.add) {
26725             this.el.update("");
26726         }
26727         this.el.mask(this.mask ? this.mask : "Loading" ); 
26728     },
26729     onLoad : function ()
26730     {
26731         this.el.unmask();
26732     },
26733     
26734
26735     /**
26736      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26737      * @param {HTMLElement} node
26738      * @return {HTMLElement} The template node
26739      */
26740     findItemFromChild : function(node){
26741         var el = this.dataName  ?
26742             this.el.child('.roo-tpl-' + this.dataName,true) :
26743             this.el.dom; 
26744         
26745         if(!node || node.parentNode == el){
26746                     return node;
26747             }
26748             var p = node.parentNode;
26749             while(p && p != el){
26750             if(p.parentNode == el){
26751                 return p;
26752             }
26753             p = p.parentNode;
26754         }
26755             return null;
26756     },
26757
26758     /** @ignore */
26759     onClick : function(e){
26760         var item = this.findItemFromChild(e.getTarget());
26761         if(item){
26762             var index = this.indexOf(item);
26763             if(this.onItemClick(item, index, e) !== false){
26764                 this.fireEvent("click", this, index, item, e);
26765             }
26766         }else{
26767             this.clearSelections();
26768         }
26769     },
26770
26771     /** @ignore */
26772     onContextMenu : function(e){
26773         var item = this.findItemFromChild(e.getTarget());
26774         if(item){
26775             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26776         }
26777     },
26778
26779     /** @ignore */
26780     onDblClick : function(e){
26781         var item = this.findItemFromChild(e.getTarget());
26782         if(item){
26783             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26784         }
26785     },
26786
26787     onItemClick : function(item, index, e)
26788     {
26789         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26790             return false;
26791         }
26792         if (this.toggleSelect) {
26793             var m = this.isSelected(item) ? 'unselect' : 'select';
26794             //Roo.log(m);
26795             var _t = this;
26796             _t[m](item, true, false);
26797             return true;
26798         }
26799         if(this.multiSelect || this.singleSelect){
26800             if(this.multiSelect && e.shiftKey && this.lastSelection){
26801                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26802             }else{
26803                 this.select(item, this.multiSelect && e.ctrlKey);
26804                 this.lastSelection = item;
26805             }
26806             
26807             if(!this.tickable){
26808                 e.preventDefault();
26809             }
26810             
26811         }
26812         return true;
26813     },
26814
26815     /**
26816      * Get the number of selected nodes.
26817      * @return {Number}
26818      */
26819     getSelectionCount : function(){
26820         return this.selections.length;
26821     },
26822
26823     /**
26824      * Get the currently selected nodes.
26825      * @return {Array} An array of HTMLElements
26826      */
26827     getSelectedNodes : function(){
26828         return this.selections;
26829     },
26830
26831     /**
26832      * Get the indexes of the selected nodes.
26833      * @return {Array}
26834      */
26835     getSelectedIndexes : function(){
26836         var indexes = [], s = this.selections;
26837         for(var i = 0, len = s.length; i < len; i++){
26838             indexes.push(s[i].nodeIndex);
26839         }
26840         return indexes;
26841     },
26842
26843     /**
26844      * Clear all selections
26845      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26846      */
26847     clearSelections : function(suppressEvent){
26848         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26849             this.cmp.elements = this.selections;
26850             this.cmp.removeClass(this.selectedClass);
26851             this.selections = [];
26852             if(!suppressEvent){
26853                 this.fireEvent("selectionchange", this, this.selections);
26854             }
26855         }
26856     },
26857
26858     /**
26859      * Returns true if the passed node is selected
26860      * @param {HTMLElement/Number} node The node or node index
26861      * @return {Boolean}
26862      */
26863     isSelected : function(node){
26864         var s = this.selections;
26865         if(s.length < 1){
26866             return false;
26867         }
26868         node = this.getNode(node);
26869         return s.indexOf(node) !== -1;
26870     },
26871
26872     /**
26873      * Selects nodes.
26874      * @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
26875      * @param {Boolean} keepExisting (optional) true to keep existing selections
26876      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26877      */
26878     select : function(nodeInfo, keepExisting, suppressEvent){
26879         if(nodeInfo instanceof Array){
26880             if(!keepExisting){
26881                 this.clearSelections(true);
26882             }
26883             for(var i = 0, len = nodeInfo.length; i < len; i++){
26884                 this.select(nodeInfo[i], true, true);
26885             }
26886             return;
26887         } 
26888         var node = this.getNode(nodeInfo);
26889         if(!node || this.isSelected(node)){
26890             return; // already selected.
26891         }
26892         if(!keepExisting){
26893             this.clearSelections(true);
26894         }
26895         
26896         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26897             Roo.fly(node).addClass(this.selectedClass);
26898             this.selections.push(node);
26899             if(!suppressEvent){
26900                 this.fireEvent("selectionchange", this, this.selections);
26901             }
26902         }
26903         
26904         
26905     },
26906       /**
26907      * Unselects nodes.
26908      * @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
26909      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26910      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26911      */
26912     unselect : function(nodeInfo, keepExisting, suppressEvent)
26913     {
26914         if(nodeInfo instanceof Array){
26915             Roo.each(this.selections, function(s) {
26916                 this.unselect(s, nodeInfo);
26917             }, this);
26918             return;
26919         }
26920         var node = this.getNode(nodeInfo);
26921         if(!node || !this.isSelected(node)){
26922             //Roo.log("not selected");
26923             return; // not selected.
26924         }
26925         // fireevent???
26926         var ns = [];
26927         Roo.each(this.selections, function(s) {
26928             if (s == node ) {
26929                 Roo.fly(node).removeClass(this.selectedClass);
26930
26931                 return;
26932             }
26933             ns.push(s);
26934         },this);
26935         
26936         this.selections= ns;
26937         this.fireEvent("selectionchange", this, this.selections);
26938     },
26939
26940     /**
26941      * Gets a template node.
26942      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26943      * @return {HTMLElement} The node or null if it wasn't found
26944      */
26945     getNode : function(nodeInfo){
26946         if(typeof nodeInfo == "string"){
26947             return document.getElementById(nodeInfo);
26948         }else if(typeof nodeInfo == "number"){
26949             return this.nodes[nodeInfo];
26950         }
26951         return nodeInfo;
26952     },
26953
26954     /**
26955      * Gets a range template nodes.
26956      * @param {Number} startIndex
26957      * @param {Number} endIndex
26958      * @return {Array} An array of nodes
26959      */
26960     getNodes : function(start, end){
26961         var ns = this.nodes;
26962         start = start || 0;
26963         end = typeof end == "undefined" ? ns.length - 1 : end;
26964         var nodes = [];
26965         if(start <= end){
26966             for(var i = start; i <= end; i++){
26967                 nodes.push(ns[i]);
26968             }
26969         } else{
26970             for(var i = start; i >= end; i--){
26971                 nodes.push(ns[i]);
26972             }
26973         }
26974         return nodes;
26975     },
26976
26977     /**
26978      * Finds the index of the passed node
26979      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26980      * @return {Number} The index of the node or -1
26981      */
26982     indexOf : function(node){
26983         node = this.getNode(node);
26984         if(typeof node.nodeIndex == "number"){
26985             return node.nodeIndex;
26986         }
26987         var ns = this.nodes;
26988         for(var i = 0, len = ns.length; i < len; i++){
26989             if(ns[i] == node){
26990                 return i;
26991             }
26992         }
26993         return -1;
26994     }
26995 });
26996 /*
26997  * Based on:
26998  * Ext JS Library 1.1.1
26999  * Copyright(c) 2006-2007, Ext JS, LLC.
27000  *
27001  * Originally Released Under LGPL - original licence link has changed is not relivant.
27002  *
27003  * Fork - LGPL
27004  * <script type="text/javascript">
27005  */
27006
27007 /**
27008  * @class Roo.JsonView
27009  * @extends Roo.View
27010  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27011 <pre><code>
27012 var view = new Roo.JsonView({
27013     container: "my-element",
27014     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27015     multiSelect: true, 
27016     jsonRoot: "data" 
27017 });
27018
27019 // listen for node click?
27020 view.on("click", function(vw, index, node, e){
27021     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27022 });
27023
27024 // direct load of JSON data
27025 view.load("foobar.php");
27026
27027 // Example from my blog list
27028 var tpl = new Roo.Template(
27029     '&lt;div class="entry"&gt;' +
27030     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27031     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27032     "&lt;/div&gt;&lt;hr /&gt;"
27033 );
27034
27035 var moreView = new Roo.JsonView({
27036     container :  "entry-list", 
27037     template : tpl,
27038     jsonRoot: "posts"
27039 });
27040 moreView.on("beforerender", this.sortEntries, this);
27041 moreView.load({
27042     url: "/blog/get-posts.php",
27043     params: "allposts=true",
27044     text: "Loading Blog Entries..."
27045 });
27046 </code></pre>
27047
27048 * Note: old code is supported with arguments : (container, template, config)
27049
27050
27051  * @constructor
27052  * Create a new JsonView
27053  * 
27054  * @param {Object} config The config object
27055  * 
27056  */
27057 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27058     
27059     
27060     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27061
27062     var um = this.el.getUpdateManager();
27063     um.setRenderer(this);
27064     um.on("update", this.onLoad, this);
27065     um.on("failure", this.onLoadException, this);
27066
27067     /**
27068      * @event beforerender
27069      * Fires before rendering of the downloaded JSON data.
27070      * @param {Roo.JsonView} this
27071      * @param {Object} data The JSON data loaded
27072      */
27073     /**
27074      * @event load
27075      * Fires when data is loaded.
27076      * @param {Roo.JsonView} this
27077      * @param {Object} data The JSON data loaded
27078      * @param {Object} response The raw Connect response object
27079      */
27080     /**
27081      * @event loadexception
27082      * Fires when loading fails.
27083      * @param {Roo.JsonView} this
27084      * @param {Object} response The raw Connect response object
27085      */
27086     this.addEvents({
27087         'beforerender' : true,
27088         'load' : true,
27089         'loadexception' : true
27090     });
27091 };
27092 Roo.extend(Roo.JsonView, Roo.View, {
27093     /**
27094      * @type {String} The root property in the loaded JSON object that contains the data
27095      */
27096     jsonRoot : "",
27097
27098     /**
27099      * Refreshes the view.
27100      */
27101     refresh : function(){
27102         this.clearSelections();
27103         this.el.update("");
27104         var html = [];
27105         var o = this.jsonData;
27106         if(o && o.length > 0){
27107             for(var i = 0, len = o.length; i < len; i++){
27108                 var data = this.prepareData(o[i], i, o);
27109                 html[html.length] = this.tpl.apply(data);
27110             }
27111         }else{
27112             html.push(this.emptyText);
27113         }
27114         this.el.update(html.join(""));
27115         this.nodes = this.el.dom.childNodes;
27116         this.updateIndexes(0);
27117     },
27118
27119     /**
27120      * 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.
27121      * @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:
27122      <pre><code>
27123      view.load({
27124          url: "your-url.php",
27125          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27126          callback: yourFunction,
27127          scope: yourObject, //(optional scope)
27128          discardUrl: false,
27129          nocache: false,
27130          text: "Loading...",
27131          timeout: 30,
27132          scripts: false
27133      });
27134      </code></pre>
27135      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27136      * 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.
27137      * @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}
27138      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27139      * @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.
27140      */
27141     load : function(){
27142         var um = this.el.getUpdateManager();
27143         um.update.apply(um, arguments);
27144     },
27145
27146     // note - render is a standard framework call...
27147     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27148     render : function(el, response){
27149         
27150         this.clearSelections();
27151         this.el.update("");
27152         var o;
27153         try{
27154             if (response != '') {
27155                 o = Roo.util.JSON.decode(response.responseText);
27156                 if(this.jsonRoot){
27157                     
27158                     o = o[this.jsonRoot];
27159                 }
27160             }
27161         } catch(e){
27162         }
27163         /**
27164          * The current JSON data or null
27165          */
27166         this.jsonData = o;
27167         this.beforeRender();
27168         this.refresh();
27169     },
27170
27171 /**
27172  * Get the number of records in the current JSON dataset
27173  * @return {Number}
27174  */
27175     getCount : function(){
27176         return this.jsonData ? this.jsonData.length : 0;
27177     },
27178
27179 /**
27180  * Returns the JSON object for the specified node(s)
27181  * @param {HTMLElement/Array} node The node or an array of nodes
27182  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27183  * you get the JSON object for the node
27184  */
27185     getNodeData : function(node){
27186         if(node instanceof Array){
27187             var data = [];
27188             for(var i = 0, len = node.length; i < len; i++){
27189                 data.push(this.getNodeData(node[i]));
27190             }
27191             return data;
27192         }
27193         return this.jsonData[this.indexOf(node)] || null;
27194     },
27195
27196     beforeRender : function(){
27197         this.snapshot = this.jsonData;
27198         if(this.sortInfo){
27199             this.sort.apply(this, this.sortInfo);
27200         }
27201         this.fireEvent("beforerender", this, this.jsonData);
27202     },
27203
27204     onLoad : function(el, o){
27205         this.fireEvent("load", this, this.jsonData, o);
27206     },
27207
27208     onLoadException : function(el, o){
27209         this.fireEvent("loadexception", this, o);
27210     },
27211
27212 /**
27213  * Filter the data by a specific property.
27214  * @param {String} property A property on your JSON objects
27215  * @param {String/RegExp} value Either string that the property values
27216  * should start with, or a RegExp to test against the property
27217  */
27218     filter : function(property, value){
27219         if(this.jsonData){
27220             var data = [];
27221             var ss = this.snapshot;
27222             if(typeof value == "string"){
27223                 var vlen = value.length;
27224                 if(vlen == 0){
27225                     this.clearFilter();
27226                     return;
27227                 }
27228                 value = value.toLowerCase();
27229                 for(var i = 0, len = ss.length; i < len; i++){
27230                     var o = ss[i];
27231                     if(o[property].substr(0, vlen).toLowerCase() == value){
27232                         data.push(o);
27233                     }
27234                 }
27235             } else if(value.exec){ // regex?
27236                 for(var i = 0, len = ss.length; i < len; i++){
27237                     var o = ss[i];
27238                     if(value.test(o[property])){
27239                         data.push(o);
27240                     }
27241                 }
27242             } else{
27243                 return;
27244             }
27245             this.jsonData = data;
27246             this.refresh();
27247         }
27248     },
27249
27250 /**
27251  * Filter by a function. The passed function will be called with each
27252  * object in the current dataset. If the function returns true the value is kept,
27253  * otherwise it is filtered.
27254  * @param {Function} fn
27255  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27256  */
27257     filterBy : function(fn, scope){
27258         if(this.jsonData){
27259             var data = [];
27260             var ss = this.snapshot;
27261             for(var i = 0, len = ss.length; i < len; i++){
27262                 var o = ss[i];
27263                 if(fn.call(scope || this, o)){
27264                     data.push(o);
27265                 }
27266             }
27267             this.jsonData = data;
27268             this.refresh();
27269         }
27270     },
27271
27272 /**
27273  * Clears the current filter.
27274  */
27275     clearFilter : function(){
27276         if(this.snapshot && this.jsonData != this.snapshot){
27277             this.jsonData = this.snapshot;
27278             this.refresh();
27279         }
27280     },
27281
27282
27283 /**
27284  * Sorts the data for this view and refreshes it.
27285  * @param {String} property A property on your JSON objects to sort on
27286  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27287  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27288  */
27289     sort : function(property, dir, sortType){
27290         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27291         if(this.jsonData){
27292             var p = property;
27293             var dsc = dir && dir.toLowerCase() == "desc";
27294             var f = function(o1, o2){
27295                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27296                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27297                 ;
27298                 if(v1 < v2){
27299                     return dsc ? +1 : -1;
27300                 } else if(v1 > v2){
27301                     return dsc ? -1 : +1;
27302                 } else{
27303                     return 0;
27304                 }
27305             };
27306             this.jsonData.sort(f);
27307             this.refresh();
27308             if(this.jsonData != this.snapshot){
27309                 this.snapshot.sort(f);
27310             }
27311         }
27312     }
27313 });/*
27314  * Based on:
27315  * Ext JS Library 1.1.1
27316  * Copyright(c) 2006-2007, Ext JS, LLC.
27317  *
27318  * Originally Released Under LGPL - original licence link has changed is not relivant.
27319  *
27320  * Fork - LGPL
27321  * <script type="text/javascript">
27322  */
27323  
27324
27325 /**
27326  * @class Roo.ColorPalette
27327  * @extends Roo.Component
27328  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27329  * Here's an example of typical usage:
27330  * <pre><code>
27331 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27332 cp.render('my-div');
27333
27334 cp.on('select', function(palette, selColor){
27335     // do something with selColor
27336 });
27337 </code></pre>
27338  * @constructor
27339  * Create a new ColorPalette
27340  * @param {Object} config The config object
27341  */
27342 Roo.ColorPalette = function(config){
27343     Roo.ColorPalette.superclass.constructor.call(this, config);
27344     this.addEvents({
27345         /**
27346              * @event select
27347              * Fires when a color is selected
27348              * @param {ColorPalette} this
27349              * @param {String} color The 6-digit color hex code (without the # symbol)
27350              */
27351         select: true
27352     });
27353
27354     if(this.handler){
27355         this.on("select", this.handler, this.scope, true);
27356     }
27357 };
27358 Roo.extend(Roo.ColorPalette, Roo.Component, {
27359     /**
27360      * @cfg {String} itemCls
27361      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27362      */
27363     itemCls : "x-color-palette",
27364     /**
27365      * @cfg {String} value
27366      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27367      * the hex codes are case-sensitive.
27368      */
27369     value : null,
27370     clickEvent:'click',
27371     // private
27372     ctype: "Roo.ColorPalette",
27373
27374     /**
27375      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27376      */
27377     allowReselect : false,
27378
27379     /**
27380      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27381      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27382      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27383      * of colors with the width setting until the box is symmetrical.</p>
27384      * <p>You can override individual colors if needed:</p>
27385      * <pre><code>
27386 var cp = new Roo.ColorPalette();
27387 cp.colors[0] = "FF0000";  // change the first box to red
27388 </code></pre>
27389
27390 Or you can provide a custom array of your own for complete control:
27391 <pre><code>
27392 var cp = new Roo.ColorPalette();
27393 cp.colors = ["000000", "993300", "333300"];
27394 </code></pre>
27395      * @type Array
27396      */
27397     colors : [
27398         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27399         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27400         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27401         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27402         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27403     ],
27404
27405     // private
27406     onRender : function(container, position){
27407         var t = new Roo.MasterTemplate(
27408             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27409         );
27410         var c = this.colors;
27411         for(var i = 0, len = c.length; i < len; i++){
27412             t.add([c[i]]);
27413         }
27414         var el = document.createElement("div");
27415         el.className = this.itemCls;
27416         t.overwrite(el);
27417         container.dom.insertBefore(el, position);
27418         this.el = Roo.get(el);
27419         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27420         if(this.clickEvent != 'click'){
27421             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27422         }
27423     },
27424
27425     // private
27426     afterRender : function(){
27427         Roo.ColorPalette.superclass.afterRender.call(this);
27428         if(this.value){
27429             var s = this.value;
27430             this.value = null;
27431             this.select(s);
27432         }
27433     },
27434
27435     // private
27436     handleClick : function(e, t){
27437         e.preventDefault();
27438         if(!this.disabled){
27439             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27440             this.select(c.toUpperCase());
27441         }
27442     },
27443
27444     /**
27445      * Selects the specified color in the palette (fires the select event)
27446      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27447      */
27448     select : function(color){
27449         color = color.replace("#", "");
27450         if(color != this.value || this.allowReselect){
27451             var el = this.el;
27452             if(this.value){
27453                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27454             }
27455             el.child("a.color-"+color).addClass("x-color-palette-sel");
27456             this.value = color;
27457             this.fireEvent("select", this, color);
27458         }
27459     }
27460 });/*
27461  * Based on:
27462  * Ext JS Library 1.1.1
27463  * Copyright(c) 2006-2007, Ext JS, LLC.
27464  *
27465  * Originally Released Under LGPL - original licence link has changed is not relivant.
27466  *
27467  * Fork - LGPL
27468  * <script type="text/javascript">
27469  */
27470  
27471 /**
27472  * @class Roo.DatePicker
27473  * @extends Roo.Component
27474  * Simple date picker class.
27475  * @constructor
27476  * Create a new DatePicker
27477  * @param {Object} config The config object
27478  */
27479 Roo.DatePicker = function(config){
27480     Roo.DatePicker.superclass.constructor.call(this, config);
27481
27482     this.value = config && config.value ?
27483                  config.value.clearTime() : new Date().clearTime();
27484
27485     this.addEvents({
27486         /**
27487              * @event select
27488              * Fires when a date is selected
27489              * @param {DatePicker} this
27490              * @param {Date} date The selected date
27491              */
27492         'select': true,
27493         /**
27494              * @event monthchange
27495              * Fires when the displayed month changes 
27496              * @param {DatePicker} this
27497              * @param {Date} date The selected month
27498              */
27499         'monthchange': true
27500     });
27501
27502     if(this.handler){
27503         this.on("select", this.handler,  this.scope || this);
27504     }
27505     // build the disabledDatesRE
27506     if(!this.disabledDatesRE && this.disabledDates){
27507         var dd = this.disabledDates;
27508         var re = "(?:";
27509         for(var i = 0; i < dd.length; i++){
27510             re += dd[i];
27511             if(i != dd.length-1) {
27512                 re += "|";
27513             }
27514         }
27515         this.disabledDatesRE = new RegExp(re + ")");
27516     }
27517 };
27518
27519 Roo.extend(Roo.DatePicker, Roo.Component, {
27520     /**
27521      * @cfg {String} todayText
27522      * The text to display on the button that selects the current date (defaults to "Today")
27523      */
27524     todayText : "Today",
27525     /**
27526      * @cfg {String} okText
27527      * The text to display on the ok button
27528      */
27529     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27530     /**
27531      * @cfg {String} cancelText
27532      * The text to display on the cancel button
27533      */
27534     cancelText : "Cancel",
27535     /**
27536      * @cfg {String} todayTip
27537      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27538      */
27539     todayTip : "{0} (Spacebar)",
27540     /**
27541      * @cfg {Date} minDate
27542      * Minimum allowable date (JavaScript date object, defaults to null)
27543      */
27544     minDate : null,
27545     /**
27546      * @cfg {Date} maxDate
27547      * Maximum allowable date (JavaScript date object, defaults to null)
27548      */
27549     maxDate : null,
27550     /**
27551      * @cfg {String} minText
27552      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27553      */
27554     minText : "This date is before the minimum date",
27555     /**
27556      * @cfg {String} maxText
27557      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27558      */
27559     maxText : "This date is after the maximum date",
27560     /**
27561      * @cfg {String} format
27562      * The default date format string which can be overriden for localization support.  The format must be
27563      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27564      */
27565     format : "m/d/y",
27566     /**
27567      * @cfg {Array} disabledDays
27568      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27569      */
27570     disabledDays : null,
27571     /**
27572      * @cfg {String} disabledDaysText
27573      * The tooltip to display when the date falls on a disabled day (defaults to "")
27574      */
27575     disabledDaysText : "",
27576     /**
27577      * @cfg {RegExp} disabledDatesRE
27578      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27579      */
27580     disabledDatesRE : null,
27581     /**
27582      * @cfg {String} disabledDatesText
27583      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27584      */
27585     disabledDatesText : "",
27586     /**
27587      * @cfg {Boolean} constrainToViewport
27588      * True to constrain the date picker to the viewport (defaults to true)
27589      */
27590     constrainToViewport : true,
27591     /**
27592      * @cfg {Array} monthNames
27593      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27594      */
27595     monthNames : Date.monthNames,
27596     /**
27597      * @cfg {Array} dayNames
27598      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27599      */
27600     dayNames : Date.dayNames,
27601     /**
27602      * @cfg {String} nextText
27603      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27604      */
27605     nextText: 'Next Month (Control+Right)',
27606     /**
27607      * @cfg {String} prevText
27608      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27609      */
27610     prevText: 'Previous Month (Control+Left)',
27611     /**
27612      * @cfg {String} monthYearText
27613      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27614      */
27615     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27616     /**
27617      * @cfg {Number} startDay
27618      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27619      */
27620     startDay : 0,
27621     /**
27622      * @cfg {Bool} showClear
27623      * Show a clear button (usefull for date form elements that can be blank.)
27624      */
27625     
27626     showClear: false,
27627     
27628     /**
27629      * Sets the value of the date field
27630      * @param {Date} value The date to set
27631      */
27632     setValue : function(value){
27633         var old = this.value;
27634         
27635         if (typeof(value) == 'string') {
27636          
27637             value = Date.parseDate(value, this.format);
27638         }
27639         if (!value) {
27640             value = new Date();
27641         }
27642         
27643         this.value = value.clearTime(true);
27644         if(this.el){
27645             this.update(this.value);
27646         }
27647     },
27648
27649     /**
27650      * Gets the current selected value of the date field
27651      * @return {Date} The selected date
27652      */
27653     getValue : function(){
27654         return this.value;
27655     },
27656
27657     // private
27658     focus : function(){
27659         if(this.el){
27660             this.update(this.activeDate);
27661         }
27662     },
27663
27664     // privateval
27665     onRender : function(container, position){
27666         
27667         var m = [
27668              '<table cellspacing="0">',
27669                 '<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>',
27670                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27671         var dn = this.dayNames;
27672         for(var i = 0; i < 7; i++){
27673             var d = this.startDay+i;
27674             if(d > 6){
27675                 d = d-7;
27676             }
27677             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27678         }
27679         m[m.length] = "</tr></thead><tbody><tr>";
27680         for(var i = 0; i < 42; i++) {
27681             if(i % 7 == 0 && i != 0){
27682                 m[m.length] = "</tr><tr>";
27683             }
27684             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27685         }
27686         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27687             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27688
27689         var el = document.createElement("div");
27690         el.className = "x-date-picker";
27691         el.innerHTML = m.join("");
27692
27693         container.dom.insertBefore(el, position);
27694
27695         this.el = Roo.get(el);
27696         this.eventEl = Roo.get(el.firstChild);
27697
27698         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27699             handler: this.showPrevMonth,
27700             scope: this,
27701             preventDefault:true,
27702             stopDefault:true
27703         });
27704
27705         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27706             handler: this.showNextMonth,
27707             scope: this,
27708             preventDefault:true,
27709             stopDefault:true
27710         });
27711
27712         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27713
27714         this.monthPicker = this.el.down('div.x-date-mp');
27715         this.monthPicker.enableDisplayMode('block');
27716         
27717         var kn = new Roo.KeyNav(this.eventEl, {
27718             "left" : function(e){
27719                 e.ctrlKey ?
27720                     this.showPrevMonth() :
27721                     this.update(this.activeDate.add("d", -1));
27722             },
27723
27724             "right" : function(e){
27725                 e.ctrlKey ?
27726                     this.showNextMonth() :
27727                     this.update(this.activeDate.add("d", 1));
27728             },
27729
27730             "up" : function(e){
27731                 e.ctrlKey ?
27732                     this.showNextYear() :
27733                     this.update(this.activeDate.add("d", -7));
27734             },
27735
27736             "down" : function(e){
27737                 e.ctrlKey ?
27738                     this.showPrevYear() :
27739                     this.update(this.activeDate.add("d", 7));
27740             },
27741
27742             "pageUp" : function(e){
27743                 this.showNextMonth();
27744             },
27745
27746             "pageDown" : function(e){
27747                 this.showPrevMonth();
27748             },
27749
27750             "enter" : function(e){
27751                 e.stopPropagation();
27752                 return true;
27753             },
27754
27755             scope : this
27756         });
27757
27758         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27759
27760         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27761
27762         this.el.unselectable();
27763         
27764         this.cells = this.el.select("table.x-date-inner tbody td");
27765         this.textNodes = this.el.query("table.x-date-inner tbody span");
27766
27767         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27768             text: "&#160;",
27769             tooltip: this.monthYearText
27770         });
27771
27772         this.mbtn.on('click', this.showMonthPicker, this);
27773         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27774
27775
27776         var today = (new Date()).dateFormat(this.format);
27777         
27778         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27779         if (this.showClear) {
27780             baseTb.add( new Roo.Toolbar.Fill());
27781         }
27782         baseTb.add({
27783             text: String.format(this.todayText, today),
27784             tooltip: String.format(this.todayTip, today),
27785             handler: this.selectToday,
27786             scope: this
27787         });
27788         
27789         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27790             
27791         //});
27792         if (this.showClear) {
27793             
27794             baseTb.add( new Roo.Toolbar.Fill());
27795             baseTb.add({
27796                 text: '&#160;',
27797                 cls: 'x-btn-icon x-btn-clear',
27798                 handler: function() {
27799                     //this.value = '';
27800                     this.fireEvent("select", this, '');
27801                 },
27802                 scope: this
27803             });
27804         }
27805         
27806         
27807         if(Roo.isIE){
27808             this.el.repaint();
27809         }
27810         this.update(this.value);
27811     },
27812
27813     createMonthPicker : function(){
27814         if(!this.monthPicker.dom.firstChild){
27815             var buf = ['<table border="0" cellspacing="0">'];
27816             for(var i = 0; i < 6; i++){
27817                 buf.push(
27818                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27819                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27820                     i == 0 ?
27821                     '<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>' :
27822                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27823                 );
27824             }
27825             buf.push(
27826                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27827                     this.okText,
27828                     '</button><button type="button" class="x-date-mp-cancel">',
27829                     this.cancelText,
27830                     '</button></td></tr>',
27831                 '</table>'
27832             );
27833             this.monthPicker.update(buf.join(''));
27834             this.monthPicker.on('click', this.onMonthClick, this);
27835             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27836
27837             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27838             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27839
27840             this.mpMonths.each(function(m, a, i){
27841                 i += 1;
27842                 if((i%2) == 0){
27843                     m.dom.xmonth = 5 + Math.round(i * .5);
27844                 }else{
27845                     m.dom.xmonth = Math.round((i-1) * .5);
27846                 }
27847             });
27848         }
27849     },
27850
27851     showMonthPicker : function(){
27852         this.createMonthPicker();
27853         var size = this.el.getSize();
27854         this.monthPicker.setSize(size);
27855         this.monthPicker.child('table').setSize(size);
27856
27857         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27858         this.updateMPMonth(this.mpSelMonth);
27859         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27860         this.updateMPYear(this.mpSelYear);
27861
27862         this.monthPicker.slideIn('t', {duration:.2});
27863     },
27864
27865     updateMPYear : function(y){
27866         this.mpyear = y;
27867         var ys = this.mpYears.elements;
27868         for(var i = 1; i <= 10; i++){
27869             var td = ys[i-1], y2;
27870             if((i%2) == 0){
27871                 y2 = y + Math.round(i * .5);
27872                 td.firstChild.innerHTML = y2;
27873                 td.xyear = y2;
27874             }else{
27875                 y2 = y - (5-Math.round(i * .5));
27876                 td.firstChild.innerHTML = y2;
27877                 td.xyear = y2;
27878             }
27879             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27880         }
27881     },
27882
27883     updateMPMonth : function(sm){
27884         this.mpMonths.each(function(m, a, i){
27885             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27886         });
27887     },
27888
27889     selectMPMonth: function(m){
27890         
27891     },
27892
27893     onMonthClick : function(e, t){
27894         e.stopEvent();
27895         var el = new Roo.Element(t), pn;
27896         if(el.is('button.x-date-mp-cancel')){
27897             this.hideMonthPicker();
27898         }
27899         else if(el.is('button.x-date-mp-ok')){
27900             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27901             this.hideMonthPicker();
27902         }
27903         else if(pn = el.up('td.x-date-mp-month', 2)){
27904             this.mpMonths.removeClass('x-date-mp-sel');
27905             pn.addClass('x-date-mp-sel');
27906             this.mpSelMonth = pn.dom.xmonth;
27907         }
27908         else if(pn = el.up('td.x-date-mp-year', 2)){
27909             this.mpYears.removeClass('x-date-mp-sel');
27910             pn.addClass('x-date-mp-sel');
27911             this.mpSelYear = pn.dom.xyear;
27912         }
27913         else if(el.is('a.x-date-mp-prev')){
27914             this.updateMPYear(this.mpyear-10);
27915         }
27916         else if(el.is('a.x-date-mp-next')){
27917             this.updateMPYear(this.mpyear+10);
27918         }
27919     },
27920
27921     onMonthDblClick : function(e, t){
27922         e.stopEvent();
27923         var el = new Roo.Element(t), pn;
27924         if(pn = el.up('td.x-date-mp-month', 2)){
27925             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27926             this.hideMonthPicker();
27927         }
27928         else if(pn = el.up('td.x-date-mp-year', 2)){
27929             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27930             this.hideMonthPicker();
27931         }
27932     },
27933
27934     hideMonthPicker : function(disableAnim){
27935         if(this.monthPicker){
27936             if(disableAnim === true){
27937                 this.monthPicker.hide();
27938             }else{
27939                 this.monthPicker.slideOut('t', {duration:.2});
27940             }
27941         }
27942     },
27943
27944     // private
27945     showPrevMonth : function(e){
27946         this.update(this.activeDate.add("mo", -1));
27947     },
27948
27949     // private
27950     showNextMonth : function(e){
27951         this.update(this.activeDate.add("mo", 1));
27952     },
27953
27954     // private
27955     showPrevYear : function(){
27956         this.update(this.activeDate.add("y", -1));
27957     },
27958
27959     // private
27960     showNextYear : function(){
27961         this.update(this.activeDate.add("y", 1));
27962     },
27963
27964     // private
27965     handleMouseWheel : function(e){
27966         var delta = e.getWheelDelta();
27967         if(delta > 0){
27968             this.showPrevMonth();
27969             e.stopEvent();
27970         } else if(delta < 0){
27971             this.showNextMonth();
27972             e.stopEvent();
27973         }
27974     },
27975
27976     // private
27977     handleDateClick : function(e, t){
27978         e.stopEvent();
27979         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
27980             this.setValue(new Date(t.dateValue));
27981             this.fireEvent("select", this, this.value);
27982         }
27983     },
27984
27985     // private
27986     selectToday : function(){
27987         this.setValue(new Date().clearTime());
27988         this.fireEvent("select", this, this.value);
27989     },
27990
27991     // private
27992     update : function(date)
27993     {
27994         var vd = this.activeDate;
27995         this.activeDate = date;
27996         if(vd && this.el){
27997             var t = date.getTime();
27998             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
27999                 this.cells.removeClass("x-date-selected");
28000                 this.cells.each(function(c){
28001                    if(c.dom.firstChild.dateValue == t){
28002                        c.addClass("x-date-selected");
28003                        setTimeout(function(){
28004                             try{c.dom.firstChild.focus();}catch(e){}
28005                        }, 50);
28006                        return false;
28007                    }
28008                 });
28009                 return;
28010             }
28011         }
28012         
28013         var days = date.getDaysInMonth();
28014         var firstOfMonth = date.getFirstDateOfMonth();
28015         var startingPos = firstOfMonth.getDay()-this.startDay;
28016
28017         if(startingPos <= this.startDay){
28018             startingPos += 7;
28019         }
28020
28021         var pm = date.add("mo", -1);
28022         var prevStart = pm.getDaysInMonth()-startingPos;
28023
28024         var cells = this.cells.elements;
28025         var textEls = this.textNodes;
28026         days += startingPos;
28027
28028         // convert everything to numbers so it's fast
28029         var day = 86400000;
28030         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28031         var today = new Date().clearTime().getTime();
28032         var sel = date.clearTime().getTime();
28033         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28034         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28035         var ddMatch = this.disabledDatesRE;
28036         var ddText = this.disabledDatesText;
28037         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28038         var ddaysText = this.disabledDaysText;
28039         var format = this.format;
28040
28041         var setCellClass = function(cal, cell){
28042             cell.title = "";
28043             var t = d.getTime();
28044             cell.firstChild.dateValue = t;
28045             if(t == today){
28046                 cell.className += " x-date-today";
28047                 cell.title = cal.todayText;
28048             }
28049             if(t == sel){
28050                 cell.className += " x-date-selected";
28051                 setTimeout(function(){
28052                     try{cell.firstChild.focus();}catch(e){}
28053                 }, 50);
28054             }
28055             // disabling
28056             if(t < min) {
28057                 cell.className = " x-date-disabled";
28058                 cell.title = cal.minText;
28059                 return;
28060             }
28061             if(t > max) {
28062                 cell.className = " x-date-disabled";
28063                 cell.title = cal.maxText;
28064                 return;
28065             }
28066             if(ddays){
28067                 if(ddays.indexOf(d.getDay()) != -1){
28068                     cell.title = ddaysText;
28069                     cell.className = " x-date-disabled";
28070                 }
28071             }
28072             if(ddMatch && format){
28073                 var fvalue = d.dateFormat(format);
28074                 if(ddMatch.test(fvalue)){
28075                     cell.title = ddText.replace("%0", fvalue);
28076                     cell.className = " x-date-disabled";
28077                 }
28078             }
28079         };
28080
28081         var i = 0;
28082         for(; i < startingPos; i++) {
28083             textEls[i].innerHTML = (++prevStart);
28084             d.setDate(d.getDate()+1);
28085             cells[i].className = "x-date-prevday";
28086             setCellClass(this, cells[i]);
28087         }
28088         for(; i < days; i++){
28089             intDay = i - startingPos + 1;
28090             textEls[i].innerHTML = (intDay);
28091             d.setDate(d.getDate()+1);
28092             cells[i].className = "x-date-active";
28093             setCellClass(this, cells[i]);
28094         }
28095         var extraDays = 0;
28096         for(; i < 42; i++) {
28097              textEls[i].innerHTML = (++extraDays);
28098              d.setDate(d.getDate()+1);
28099              cells[i].className = "x-date-nextday";
28100              setCellClass(this, cells[i]);
28101         }
28102
28103         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28104         this.fireEvent('monthchange', this, date);
28105         
28106         if(!this.internalRender){
28107             var main = this.el.dom.firstChild;
28108             var w = main.offsetWidth;
28109             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28110             Roo.fly(main).setWidth(w);
28111             this.internalRender = true;
28112             // opera does not respect the auto grow header center column
28113             // then, after it gets a width opera refuses to recalculate
28114             // without a second pass
28115             if(Roo.isOpera && !this.secondPass){
28116                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28117                 this.secondPass = true;
28118                 this.update.defer(10, this, [date]);
28119             }
28120         }
28121         
28122         
28123     }
28124 });        /*
28125  * Based on:
28126  * Ext JS Library 1.1.1
28127  * Copyright(c) 2006-2007, Ext JS, LLC.
28128  *
28129  * Originally Released Under LGPL - original licence link has changed is not relivant.
28130  *
28131  * Fork - LGPL
28132  * <script type="text/javascript">
28133  */
28134 /**
28135  * @class Roo.TabPanel
28136  * @extends Roo.util.Observable
28137  * A lightweight tab container.
28138  * <br><br>
28139  * Usage:
28140  * <pre><code>
28141 // basic tabs 1, built from existing content
28142 var tabs = new Roo.TabPanel("tabs1");
28143 tabs.addTab("script", "View Script");
28144 tabs.addTab("markup", "View Markup");
28145 tabs.activate("script");
28146
28147 // more advanced tabs, built from javascript
28148 var jtabs = new Roo.TabPanel("jtabs");
28149 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28150
28151 // set up the UpdateManager
28152 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28153 var updater = tab2.getUpdateManager();
28154 updater.setDefaultUrl("ajax1.htm");
28155 tab2.on('activate', updater.refresh, updater, true);
28156
28157 // Use setUrl for Ajax loading
28158 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28159 tab3.setUrl("ajax2.htm", null, true);
28160
28161 // Disabled tab
28162 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28163 tab4.disable();
28164
28165 jtabs.activate("jtabs-1");
28166  * </code></pre>
28167  * @constructor
28168  * Create a new TabPanel.
28169  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28170  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28171  */
28172 Roo.TabPanel = function(container, config){
28173     /**
28174     * The container element for this TabPanel.
28175     * @type Roo.Element
28176     */
28177     this.el = Roo.get(container, true);
28178     if(config){
28179         if(typeof config == "boolean"){
28180             this.tabPosition = config ? "bottom" : "top";
28181         }else{
28182             Roo.apply(this, config);
28183         }
28184     }
28185     if(this.tabPosition == "bottom"){
28186         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28187         this.el.addClass("x-tabs-bottom");
28188     }
28189     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28190     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28191     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28192     if(Roo.isIE){
28193         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28194     }
28195     if(this.tabPosition != "bottom"){
28196         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28197          * @type Roo.Element
28198          */
28199         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28200         this.el.addClass("x-tabs-top");
28201     }
28202     this.items = [];
28203
28204     this.bodyEl.setStyle("position", "relative");
28205
28206     this.active = null;
28207     this.activateDelegate = this.activate.createDelegate(this);
28208
28209     this.addEvents({
28210         /**
28211          * @event tabchange
28212          * Fires when the active tab changes
28213          * @param {Roo.TabPanel} this
28214          * @param {Roo.TabPanelItem} activePanel The new active tab
28215          */
28216         "tabchange": true,
28217         /**
28218          * @event beforetabchange
28219          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28220          * @param {Roo.TabPanel} this
28221          * @param {Object} e Set cancel to true on this object to cancel the tab change
28222          * @param {Roo.TabPanelItem} tab The tab being changed to
28223          */
28224         "beforetabchange" : true
28225     });
28226
28227     Roo.EventManager.onWindowResize(this.onResize, this);
28228     this.cpad = this.el.getPadding("lr");
28229     this.hiddenCount = 0;
28230
28231
28232     // toolbar on the tabbar support...
28233     if (this.toolbar) {
28234         var tcfg = this.toolbar;
28235         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28236         this.toolbar = new Roo.Toolbar(tcfg);
28237         if (Roo.isSafari) {
28238             var tbl = tcfg.container.child('table', true);
28239             tbl.setAttribute('width', '100%');
28240         }
28241         
28242     }
28243    
28244
28245
28246     Roo.TabPanel.superclass.constructor.call(this);
28247 };
28248
28249 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28250     /*
28251      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28252      */
28253     tabPosition : "top",
28254     /*
28255      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28256      */
28257     currentTabWidth : 0,
28258     /*
28259      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28260      */
28261     minTabWidth : 40,
28262     /*
28263      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28264      */
28265     maxTabWidth : 250,
28266     /*
28267      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28268      */
28269     preferredTabWidth : 175,
28270     /*
28271      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28272      */
28273     resizeTabs : false,
28274     /*
28275      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28276      */
28277     monitorResize : true,
28278     /*
28279      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28280      */
28281     toolbar : false,
28282
28283     /**
28284      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28285      * @param {String} id The id of the div to use <b>or create</b>
28286      * @param {String} text The text for the tab
28287      * @param {String} content (optional) Content to put in the TabPanelItem body
28288      * @param {Boolean} closable (optional) True to create a close icon on the tab
28289      * @return {Roo.TabPanelItem} The created TabPanelItem
28290      */
28291     addTab : function(id, text, content, closable){
28292         var item = new Roo.TabPanelItem(this, id, text, closable);
28293         this.addTabItem(item);
28294         if(content){
28295             item.setContent(content);
28296         }
28297         return item;
28298     },
28299
28300     /**
28301      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28302      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28303      * @return {Roo.TabPanelItem}
28304      */
28305     getTab : function(id){
28306         return this.items[id];
28307     },
28308
28309     /**
28310      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28311      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28312      */
28313     hideTab : function(id){
28314         var t = this.items[id];
28315         if(!t.isHidden()){
28316            t.setHidden(true);
28317            this.hiddenCount++;
28318            this.autoSizeTabs();
28319         }
28320     },
28321
28322     /**
28323      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28324      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28325      */
28326     unhideTab : function(id){
28327         var t = this.items[id];
28328         if(t.isHidden()){
28329            t.setHidden(false);
28330            this.hiddenCount--;
28331            this.autoSizeTabs();
28332         }
28333     },
28334
28335     /**
28336      * Adds an existing {@link Roo.TabPanelItem}.
28337      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28338      */
28339     addTabItem : function(item){
28340         this.items[item.id] = item;
28341         this.items.push(item);
28342         if(this.resizeTabs){
28343            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28344            this.autoSizeTabs();
28345         }else{
28346             item.autoSize();
28347         }
28348     },
28349
28350     /**
28351      * Removes a {@link Roo.TabPanelItem}.
28352      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28353      */
28354     removeTab : function(id){
28355         var items = this.items;
28356         var tab = items[id];
28357         if(!tab) { return; }
28358         var index = items.indexOf(tab);
28359         if(this.active == tab && items.length > 1){
28360             var newTab = this.getNextAvailable(index);
28361             if(newTab) {
28362                 newTab.activate();
28363             }
28364         }
28365         this.stripEl.dom.removeChild(tab.pnode.dom);
28366         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28367             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28368         }
28369         items.splice(index, 1);
28370         delete this.items[tab.id];
28371         tab.fireEvent("close", tab);
28372         tab.purgeListeners();
28373         this.autoSizeTabs();
28374     },
28375
28376     getNextAvailable : function(start){
28377         var items = this.items;
28378         var index = start;
28379         // look for a next tab that will slide over to
28380         // replace the one being removed
28381         while(index < items.length){
28382             var item = items[++index];
28383             if(item && !item.isHidden()){
28384                 return item;
28385             }
28386         }
28387         // if one isn't found select the previous tab (on the left)
28388         index = start;
28389         while(index >= 0){
28390             var item = items[--index];
28391             if(item && !item.isHidden()){
28392                 return item;
28393             }
28394         }
28395         return null;
28396     },
28397
28398     /**
28399      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28400      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28401      */
28402     disableTab : function(id){
28403         var tab = this.items[id];
28404         if(tab && this.active != tab){
28405             tab.disable();
28406         }
28407     },
28408
28409     /**
28410      * Enables a {@link Roo.TabPanelItem} that is disabled.
28411      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28412      */
28413     enableTab : function(id){
28414         var tab = this.items[id];
28415         tab.enable();
28416     },
28417
28418     /**
28419      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28420      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28421      * @return {Roo.TabPanelItem} The TabPanelItem.
28422      */
28423     activate : function(id){
28424         var tab = this.items[id];
28425         if(!tab){
28426             return null;
28427         }
28428         if(tab == this.active || tab.disabled){
28429             return tab;
28430         }
28431         var e = {};
28432         this.fireEvent("beforetabchange", this, e, tab);
28433         if(e.cancel !== true && !tab.disabled){
28434             if(this.active){
28435                 this.active.hide();
28436             }
28437             this.active = this.items[id];
28438             this.active.show();
28439             this.fireEvent("tabchange", this, this.active);
28440         }
28441         return tab;
28442     },
28443
28444     /**
28445      * Gets the active {@link Roo.TabPanelItem}.
28446      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28447      */
28448     getActiveTab : function(){
28449         return this.active;
28450     },
28451
28452     /**
28453      * Updates the tab body element to fit the height of the container element
28454      * for overflow scrolling
28455      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28456      */
28457     syncHeight : function(targetHeight){
28458         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28459         var bm = this.bodyEl.getMargins();
28460         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28461         this.bodyEl.setHeight(newHeight);
28462         return newHeight;
28463     },
28464
28465     onResize : function(){
28466         if(this.monitorResize){
28467             this.autoSizeTabs();
28468         }
28469     },
28470
28471     /**
28472      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28473      */
28474     beginUpdate : function(){
28475         this.updating = true;
28476     },
28477
28478     /**
28479      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28480      */
28481     endUpdate : function(){
28482         this.updating = false;
28483         this.autoSizeTabs();
28484     },
28485
28486     /**
28487      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28488      */
28489     autoSizeTabs : function(){
28490         var count = this.items.length;
28491         var vcount = count - this.hiddenCount;
28492         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28493             return;
28494         }
28495         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28496         var availWidth = Math.floor(w / vcount);
28497         var b = this.stripBody;
28498         if(b.getWidth() > w){
28499             var tabs = this.items;
28500             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28501             if(availWidth < this.minTabWidth){
28502                 /*if(!this.sleft){    // incomplete scrolling code
28503                     this.createScrollButtons();
28504                 }
28505                 this.showScroll();
28506                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28507             }
28508         }else{
28509             if(this.currentTabWidth < this.preferredTabWidth){
28510                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28511             }
28512         }
28513     },
28514
28515     /**
28516      * Returns the number of tabs in this TabPanel.
28517      * @return {Number}
28518      */
28519      getCount : function(){
28520          return this.items.length;
28521      },
28522
28523     /**
28524      * Resizes all the tabs to the passed width
28525      * @param {Number} The new width
28526      */
28527     setTabWidth : function(width){
28528         this.currentTabWidth = width;
28529         for(var i = 0, len = this.items.length; i < len; i++) {
28530                 if(!this.items[i].isHidden()) {
28531                 this.items[i].setWidth(width);
28532             }
28533         }
28534     },
28535
28536     /**
28537      * Destroys this TabPanel
28538      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28539      */
28540     destroy : function(removeEl){
28541         Roo.EventManager.removeResizeListener(this.onResize, this);
28542         for(var i = 0, len = this.items.length; i < len; i++){
28543             this.items[i].purgeListeners();
28544         }
28545         if(removeEl === true){
28546             this.el.update("");
28547             this.el.remove();
28548         }
28549     }
28550 });
28551
28552 /**
28553  * @class Roo.TabPanelItem
28554  * @extends Roo.util.Observable
28555  * Represents an individual item (tab plus body) in a TabPanel.
28556  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28557  * @param {String} id The id of this TabPanelItem
28558  * @param {String} text The text for the tab of this TabPanelItem
28559  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28560  */
28561 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28562     /**
28563      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28564      * @type Roo.TabPanel
28565      */
28566     this.tabPanel = tabPanel;
28567     /**
28568      * The id for this TabPanelItem
28569      * @type String
28570      */
28571     this.id = id;
28572     /** @private */
28573     this.disabled = false;
28574     /** @private */
28575     this.text = text;
28576     /** @private */
28577     this.loaded = false;
28578     this.closable = closable;
28579
28580     /**
28581      * The body element for this TabPanelItem.
28582      * @type Roo.Element
28583      */
28584     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28585     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28586     this.bodyEl.setStyle("display", "block");
28587     this.bodyEl.setStyle("zoom", "1");
28588     this.hideAction();
28589
28590     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28591     /** @private */
28592     this.el = Roo.get(els.el, true);
28593     this.inner = Roo.get(els.inner, true);
28594     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28595     this.pnode = Roo.get(els.el.parentNode, true);
28596     this.el.on("mousedown", this.onTabMouseDown, this);
28597     this.el.on("click", this.onTabClick, this);
28598     /** @private */
28599     if(closable){
28600         var c = Roo.get(els.close, true);
28601         c.dom.title = this.closeText;
28602         c.addClassOnOver("close-over");
28603         c.on("click", this.closeClick, this);
28604      }
28605
28606     this.addEvents({
28607          /**
28608          * @event activate
28609          * Fires when this tab becomes the active tab.
28610          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28611          * @param {Roo.TabPanelItem} this
28612          */
28613         "activate": true,
28614         /**
28615          * @event beforeclose
28616          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28617          * @param {Roo.TabPanelItem} this
28618          * @param {Object} e Set cancel to true on this object to cancel the close.
28619          */
28620         "beforeclose": true,
28621         /**
28622          * @event close
28623          * Fires when this tab is closed.
28624          * @param {Roo.TabPanelItem} this
28625          */
28626          "close": true,
28627         /**
28628          * @event deactivate
28629          * Fires when this tab is no longer the active tab.
28630          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28631          * @param {Roo.TabPanelItem} this
28632          */
28633          "deactivate" : true
28634     });
28635     this.hidden = false;
28636
28637     Roo.TabPanelItem.superclass.constructor.call(this);
28638 };
28639
28640 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28641     purgeListeners : function(){
28642        Roo.util.Observable.prototype.purgeListeners.call(this);
28643        this.el.removeAllListeners();
28644     },
28645     /**
28646      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28647      */
28648     show : function(){
28649         this.pnode.addClass("on");
28650         this.showAction();
28651         if(Roo.isOpera){
28652             this.tabPanel.stripWrap.repaint();
28653         }
28654         this.fireEvent("activate", this.tabPanel, this);
28655     },
28656
28657     /**
28658      * Returns true if this tab is the active tab.
28659      * @return {Boolean}
28660      */
28661     isActive : function(){
28662         return this.tabPanel.getActiveTab() == this;
28663     },
28664
28665     /**
28666      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28667      */
28668     hide : function(){
28669         this.pnode.removeClass("on");
28670         this.hideAction();
28671         this.fireEvent("deactivate", this.tabPanel, this);
28672     },
28673
28674     hideAction : function(){
28675         this.bodyEl.hide();
28676         this.bodyEl.setStyle("position", "absolute");
28677         this.bodyEl.setLeft("-20000px");
28678         this.bodyEl.setTop("-20000px");
28679     },
28680
28681     showAction : function(){
28682         this.bodyEl.setStyle("position", "relative");
28683         this.bodyEl.setTop("");
28684         this.bodyEl.setLeft("");
28685         this.bodyEl.show();
28686     },
28687
28688     /**
28689      * Set the tooltip for the tab.
28690      * @param {String} tooltip The tab's tooltip
28691      */
28692     setTooltip : function(text){
28693         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28694             this.textEl.dom.qtip = text;
28695             this.textEl.dom.removeAttribute('title');
28696         }else{
28697             this.textEl.dom.title = text;
28698         }
28699     },
28700
28701     onTabClick : function(e){
28702         e.preventDefault();
28703         this.tabPanel.activate(this.id);
28704     },
28705
28706     onTabMouseDown : function(e){
28707         e.preventDefault();
28708         this.tabPanel.activate(this.id);
28709     },
28710
28711     getWidth : function(){
28712         return this.inner.getWidth();
28713     },
28714
28715     setWidth : function(width){
28716         var iwidth = width - this.pnode.getPadding("lr");
28717         this.inner.setWidth(iwidth);
28718         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28719         this.pnode.setWidth(width);
28720     },
28721
28722     /**
28723      * Show or hide the tab
28724      * @param {Boolean} hidden True to hide or false to show.
28725      */
28726     setHidden : function(hidden){
28727         this.hidden = hidden;
28728         this.pnode.setStyle("display", hidden ? "none" : "");
28729     },
28730
28731     /**
28732      * Returns true if this tab is "hidden"
28733      * @return {Boolean}
28734      */
28735     isHidden : function(){
28736         return this.hidden;
28737     },
28738
28739     /**
28740      * Returns the text for this tab
28741      * @return {String}
28742      */
28743     getText : function(){
28744         return this.text;
28745     },
28746
28747     autoSize : function(){
28748         //this.el.beginMeasure();
28749         this.textEl.setWidth(1);
28750         /*
28751          *  #2804 [new] Tabs in Roojs
28752          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28753          */
28754         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28755         //this.el.endMeasure();
28756     },
28757
28758     /**
28759      * Sets the text for the tab (Note: this also sets the tooltip text)
28760      * @param {String} text The tab's text and tooltip
28761      */
28762     setText : function(text){
28763         this.text = text;
28764         this.textEl.update(text);
28765         this.setTooltip(text);
28766         if(!this.tabPanel.resizeTabs){
28767             this.autoSize();
28768         }
28769     },
28770     /**
28771      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28772      */
28773     activate : function(){
28774         this.tabPanel.activate(this.id);
28775     },
28776
28777     /**
28778      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28779      */
28780     disable : function(){
28781         if(this.tabPanel.active != this){
28782             this.disabled = true;
28783             this.pnode.addClass("disabled");
28784         }
28785     },
28786
28787     /**
28788      * Enables this TabPanelItem if it was previously disabled.
28789      */
28790     enable : function(){
28791         this.disabled = false;
28792         this.pnode.removeClass("disabled");
28793     },
28794
28795     /**
28796      * Sets the content for this TabPanelItem.
28797      * @param {String} content The content
28798      * @param {Boolean} loadScripts true to look for and load scripts
28799      */
28800     setContent : function(content, loadScripts){
28801         this.bodyEl.update(content, loadScripts);
28802     },
28803
28804     /**
28805      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28806      * @return {Roo.UpdateManager} The UpdateManager
28807      */
28808     getUpdateManager : function(){
28809         return this.bodyEl.getUpdateManager();
28810     },
28811
28812     /**
28813      * Set a URL to be used to load the content for this TabPanelItem.
28814      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28815      * @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)
28816      * @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)
28817      * @return {Roo.UpdateManager} The UpdateManager
28818      */
28819     setUrl : function(url, params, loadOnce){
28820         if(this.refreshDelegate){
28821             this.un('activate', this.refreshDelegate);
28822         }
28823         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28824         this.on("activate", this.refreshDelegate);
28825         return this.bodyEl.getUpdateManager();
28826     },
28827
28828     /** @private */
28829     _handleRefresh : function(url, params, loadOnce){
28830         if(!loadOnce || !this.loaded){
28831             var updater = this.bodyEl.getUpdateManager();
28832             updater.update(url, params, this._setLoaded.createDelegate(this));
28833         }
28834     },
28835
28836     /**
28837      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28838      *   Will fail silently if the setUrl method has not been called.
28839      *   This does not activate the panel, just updates its content.
28840      */
28841     refresh : function(){
28842         if(this.refreshDelegate){
28843            this.loaded = false;
28844            this.refreshDelegate();
28845         }
28846     },
28847
28848     /** @private */
28849     _setLoaded : function(){
28850         this.loaded = true;
28851     },
28852
28853     /** @private */
28854     closeClick : function(e){
28855         var o = {};
28856         e.stopEvent();
28857         this.fireEvent("beforeclose", this, o);
28858         if(o.cancel !== true){
28859             this.tabPanel.removeTab(this.id);
28860         }
28861     },
28862     /**
28863      * The text displayed in the tooltip for the close icon.
28864      * @type String
28865      */
28866     closeText : "Close this tab"
28867 });
28868
28869 /** @private */
28870 Roo.TabPanel.prototype.createStrip = function(container){
28871     var strip = document.createElement("div");
28872     strip.className = "x-tabs-wrap";
28873     container.appendChild(strip);
28874     return strip;
28875 };
28876 /** @private */
28877 Roo.TabPanel.prototype.createStripList = function(strip){
28878     // div wrapper for retard IE
28879     // returns the "tr" element.
28880     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28881         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28882         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28883     return strip.firstChild.firstChild.firstChild.firstChild;
28884 };
28885 /** @private */
28886 Roo.TabPanel.prototype.createBody = function(container){
28887     var body = document.createElement("div");
28888     Roo.id(body, "tab-body");
28889     Roo.fly(body).addClass("x-tabs-body");
28890     container.appendChild(body);
28891     return body;
28892 };
28893 /** @private */
28894 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28895     var body = Roo.getDom(id);
28896     if(!body){
28897         body = document.createElement("div");
28898         body.id = id;
28899     }
28900     Roo.fly(body).addClass("x-tabs-item-body");
28901     bodyEl.insertBefore(body, bodyEl.firstChild);
28902     return body;
28903 };
28904 /** @private */
28905 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28906     var td = document.createElement("td");
28907     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28908     //stripEl.appendChild(td);
28909     if(closable){
28910         td.className = "x-tabs-closable";
28911         if(!this.closeTpl){
28912             this.closeTpl = new Roo.Template(
28913                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28914                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28915                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28916             );
28917         }
28918         var el = this.closeTpl.overwrite(td, {"text": text});
28919         var close = el.getElementsByTagName("div")[0];
28920         var inner = el.getElementsByTagName("em")[0];
28921         return {"el": el, "close": close, "inner": inner};
28922     } else {
28923         if(!this.tabTpl){
28924             this.tabTpl = new Roo.Template(
28925                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28926                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28927             );
28928         }
28929         var el = this.tabTpl.overwrite(td, {"text": text});
28930         var inner = el.getElementsByTagName("em")[0];
28931         return {"el": el, "inner": inner};
28932     }
28933 };/*
28934  * Based on:
28935  * Ext JS Library 1.1.1
28936  * Copyright(c) 2006-2007, Ext JS, LLC.
28937  *
28938  * Originally Released Under LGPL - original licence link has changed is not relivant.
28939  *
28940  * Fork - LGPL
28941  * <script type="text/javascript">
28942  */
28943
28944 /**
28945  * @class Roo.Button
28946  * @extends Roo.util.Observable
28947  * Simple Button class
28948  * @cfg {String} text The button text
28949  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28950  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28951  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28952  * @cfg {Object} scope The scope of the handler
28953  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28954  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
28955  * @cfg {Boolean} hidden True to start hidden (defaults to false)
28956  * @cfg {Boolean} disabled True to start disabled (defaults to false)
28957  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
28958  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
28959    applies if enableToggle = true)
28960  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
28961  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
28962   an {@link Roo.util.ClickRepeater} config object (defaults to false).
28963  * @constructor
28964  * Create a new button
28965  * @param {Object} config The config object
28966  */
28967 Roo.Button = function(renderTo, config)
28968 {
28969     if (!config) {
28970         config = renderTo;
28971         renderTo = config.renderTo || false;
28972     }
28973     
28974     Roo.apply(this, config);
28975     this.addEvents({
28976         /**
28977              * @event click
28978              * Fires when this button is clicked
28979              * @param {Button} this
28980              * @param {EventObject} e The click event
28981              */
28982             "click" : true,
28983         /**
28984              * @event toggle
28985              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
28986              * @param {Button} this
28987              * @param {Boolean} pressed
28988              */
28989             "toggle" : true,
28990         /**
28991              * @event mouseover
28992              * Fires when the mouse hovers over the button
28993              * @param {Button} this
28994              * @param {Event} e The event object
28995              */
28996         'mouseover' : true,
28997         /**
28998              * @event mouseout
28999              * Fires when the mouse exits the button
29000              * @param {Button} this
29001              * @param {Event} e The event object
29002              */
29003         'mouseout': true,
29004          /**
29005              * @event render
29006              * Fires when the button is rendered
29007              * @param {Button} this
29008              */
29009         'render': true
29010     });
29011     if(this.menu){
29012         this.menu = Roo.menu.MenuMgr.get(this.menu);
29013     }
29014     // register listeners first!!  - so render can be captured..
29015     Roo.util.Observable.call(this);
29016     if(renderTo){
29017         this.render(renderTo);
29018     }
29019     
29020   
29021 };
29022
29023 Roo.extend(Roo.Button, Roo.util.Observable, {
29024     /**
29025      * 
29026      */
29027     
29028     /**
29029      * Read-only. True if this button is hidden
29030      * @type Boolean
29031      */
29032     hidden : false,
29033     /**
29034      * Read-only. True if this button is disabled
29035      * @type Boolean
29036      */
29037     disabled : false,
29038     /**
29039      * Read-only. True if this button is pressed (only if enableToggle = true)
29040      * @type Boolean
29041      */
29042     pressed : false,
29043
29044     /**
29045      * @cfg {Number} tabIndex 
29046      * The DOM tabIndex for this button (defaults to undefined)
29047      */
29048     tabIndex : undefined,
29049
29050     /**
29051      * @cfg {Boolean} enableToggle
29052      * True to enable pressed/not pressed toggling (defaults to false)
29053      */
29054     enableToggle: false,
29055     /**
29056      * @cfg {Mixed} menu
29057      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29058      */
29059     menu : undefined,
29060     /**
29061      * @cfg {String} menuAlign
29062      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29063      */
29064     menuAlign : "tl-bl?",
29065
29066     /**
29067      * @cfg {String} iconCls
29068      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29069      */
29070     iconCls : undefined,
29071     /**
29072      * @cfg {String} type
29073      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29074      */
29075     type : 'button',
29076
29077     // private
29078     menuClassTarget: 'tr',
29079
29080     /**
29081      * @cfg {String} clickEvent
29082      * The type of event to map to the button's event handler (defaults to 'click')
29083      */
29084     clickEvent : 'click',
29085
29086     /**
29087      * @cfg {Boolean} handleMouseEvents
29088      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29089      */
29090     handleMouseEvents : true,
29091
29092     /**
29093      * @cfg {String} tooltipType
29094      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29095      */
29096     tooltipType : 'qtip',
29097
29098     /**
29099      * @cfg {String} cls
29100      * A CSS class to apply to the button's main element.
29101      */
29102     
29103     /**
29104      * @cfg {Roo.Template} template (Optional)
29105      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29106      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29107      * require code modifications if required elements (e.g. a button) aren't present.
29108      */
29109
29110     // private
29111     render : function(renderTo){
29112         var btn;
29113         if(this.hideParent){
29114             this.parentEl = Roo.get(renderTo);
29115         }
29116         if(!this.dhconfig){
29117             if(!this.template){
29118                 if(!Roo.Button.buttonTemplate){
29119                     // hideous table template
29120                     Roo.Button.buttonTemplate = new Roo.Template(
29121                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29122                         '<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>',
29123                         "</tr></tbody></table>");
29124                 }
29125                 this.template = Roo.Button.buttonTemplate;
29126             }
29127             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29128             var btnEl = btn.child("button:first");
29129             btnEl.on('focus', this.onFocus, this);
29130             btnEl.on('blur', this.onBlur, this);
29131             if(this.cls){
29132                 btn.addClass(this.cls);
29133             }
29134             if(this.icon){
29135                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29136             }
29137             if(this.iconCls){
29138                 btnEl.addClass(this.iconCls);
29139                 if(!this.cls){
29140                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29141                 }
29142             }
29143             if(this.tabIndex !== undefined){
29144                 btnEl.dom.tabIndex = this.tabIndex;
29145             }
29146             if(this.tooltip){
29147                 if(typeof this.tooltip == 'object'){
29148                     Roo.QuickTips.tips(Roo.apply({
29149                           target: btnEl.id
29150                     }, this.tooltip));
29151                 } else {
29152                     btnEl.dom[this.tooltipType] = this.tooltip;
29153                 }
29154             }
29155         }else{
29156             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29157         }
29158         this.el = btn;
29159         if(this.id){
29160             this.el.dom.id = this.el.id = this.id;
29161         }
29162         if(this.menu){
29163             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29164             this.menu.on("show", this.onMenuShow, this);
29165             this.menu.on("hide", this.onMenuHide, this);
29166         }
29167         btn.addClass("x-btn");
29168         if(Roo.isIE && !Roo.isIE7){
29169             this.autoWidth.defer(1, this);
29170         }else{
29171             this.autoWidth();
29172         }
29173         if(this.handleMouseEvents){
29174             btn.on("mouseover", this.onMouseOver, this);
29175             btn.on("mouseout", this.onMouseOut, this);
29176             btn.on("mousedown", this.onMouseDown, this);
29177         }
29178         btn.on(this.clickEvent, this.onClick, this);
29179         //btn.on("mouseup", this.onMouseUp, this);
29180         if(this.hidden){
29181             this.hide();
29182         }
29183         if(this.disabled){
29184             this.disable();
29185         }
29186         Roo.ButtonToggleMgr.register(this);
29187         if(this.pressed){
29188             this.el.addClass("x-btn-pressed");
29189         }
29190         if(this.repeat){
29191             var repeater = new Roo.util.ClickRepeater(btn,
29192                 typeof this.repeat == "object" ? this.repeat : {}
29193             );
29194             repeater.on("click", this.onClick,  this);
29195         }
29196         
29197         this.fireEvent('render', this);
29198         
29199     },
29200     /**
29201      * Returns the button's underlying element
29202      * @return {Roo.Element} The element
29203      */
29204     getEl : function(){
29205         return this.el;  
29206     },
29207     
29208     /**
29209      * Destroys this Button and removes any listeners.
29210      */
29211     destroy : function(){
29212         Roo.ButtonToggleMgr.unregister(this);
29213         this.el.removeAllListeners();
29214         this.purgeListeners();
29215         this.el.remove();
29216     },
29217
29218     // private
29219     autoWidth : function(){
29220         if(this.el){
29221             this.el.setWidth("auto");
29222             if(Roo.isIE7 && Roo.isStrict){
29223                 var ib = this.el.child('button');
29224                 if(ib && ib.getWidth() > 20){
29225                     ib.clip();
29226                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29227                 }
29228             }
29229             if(this.minWidth){
29230                 if(this.hidden){
29231                     this.el.beginMeasure();
29232                 }
29233                 if(this.el.getWidth() < this.minWidth){
29234                     this.el.setWidth(this.minWidth);
29235                 }
29236                 if(this.hidden){
29237                     this.el.endMeasure();
29238                 }
29239             }
29240         }
29241     },
29242
29243     /**
29244      * Assigns this button's click handler
29245      * @param {Function} handler The function to call when the button is clicked
29246      * @param {Object} scope (optional) Scope for the function passed in
29247      */
29248     setHandler : function(handler, scope){
29249         this.handler = handler;
29250         this.scope = scope;  
29251     },
29252     
29253     /**
29254      * Sets this button's text
29255      * @param {String} text The button text
29256      */
29257     setText : function(text){
29258         this.text = text;
29259         if(this.el){
29260             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29261         }
29262         this.autoWidth();
29263     },
29264     
29265     /**
29266      * Gets the text for this button
29267      * @return {String} The button text
29268      */
29269     getText : function(){
29270         return this.text;  
29271     },
29272     
29273     /**
29274      * Show this button
29275      */
29276     show: function(){
29277         this.hidden = false;
29278         if(this.el){
29279             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29280         }
29281     },
29282     
29283     /**
29284      * Hide this button
29285      */
29286     hide: function(){
29287         this.hidden = true;
29288         if(this.el){
29289             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29290         }
29291     },
29292     
29293     /**
29294      * Convenience function for boolean show/hide
29295      * @param {Boolean} visible True to show, false to hide
29296      */
29297     setVisible: function(visible){
29298         if(visible) {
29299             this.show();
29300         }else{
29301             this.hide();
29302         }
29303     },
29304     
29305     /**
29306      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29307      * @param {Boolean} state (optional) Force a particular state
29308      */
29309     toggle : function(state){
29310         state = state === undefined ? !this.pressed : state;
29311         if(state != this.pressed){
29312             if(state){
29313                 this.el.addClass("x-btn-pressed");
29314                 this.pressed = true;
29315                 this.fireEvent("toggle", this, true);
29316             }else{
29317                 this.el.removeClass("x-btn-pressed");
29318                 this.pressed = false;
29319                 this.fireEvent("toggle", this, false);
29320             }
29321             if(this.toggleHandler){
29322                 this.toggleHandler.call(this.scope || this, this, state);
29323             }
29324         }
29325     },
29326     
29327     /**
29328      * Focus the button
29329      */
29330     focus : function(){
29331         this.el.child('button:first').focus();
29332     },
29333     
29334     /**
29335      * Disable this button
29336      */
29337     disable : function(){
29338         if(this.el){
29339             this.el.addClass("x-btn-disabled");
29340         }
29341         this.disabled = true;
29342     },
29343     
29344     /**
29345      * Enable this button
29346      */
29347     enable : function(){
29348         if(this.el){
29349             this.el.removeClass("x-btn-disabled");
29350         }
29351         this.disabled = false;
29352     },
29353
29354     /**
29355      * Convenience function for boolean enable/disable
29356      * @param {Boolean} enabled True to enable, false to disable
29357      */
29358     setDisabled : function(v){
29359         this[v !== true ? "enable" : "disable"]();
29360     },
29361
29362     // private
29363     onClick : function(e)
29364     {
29365         if(e){
29366             e.preventDefault();
29367         }
29368         if(e.button != 0){
29369             return;
29370         }
29371         if(!this.disabled){
29372             if(this.enableToggle){
29373                 this.toggle();
29374             }
29375             if(this.menu && !this.menu.isVisible()){
29376                 this.menu.show(this.el, this.menuAlign);
29377             }
29378             this.fireEvent("click", this, e);
29379             if(this.handler){
29380                 this.el.removeClass("x-btn-over");
29381                 this.handler.call(this.scope || this, this, e);
29382             }
29383         }
29384     },
29385     // private
29386     onMouseOver : function(e){
29387         if(!this.disabled){
29388             this.el.addClass("x-btn-over");
29389             this.fireEvent('mouseover', this, e);
29390         }
29391     },
29392     // private
29393     onMouseOut : function(e){
29394         if(!e.within(this.el,  true)){
29395             this.el.removeClass("x-btn-over");
29396             this.fireEvent('mouseout', this, e);
29397         }
29398     },
29399     // private
29400     onFocus : function(e){
29401         if(!this.disabled){
29402             this.el.addClass("x-btn-focus");
29403         }
29404     },
29405     // private
29406     onBlur : function(e){
29407         this.el.removeClass("x-btn-focus");
29408     },
29409     // private
29410     onMouseDown : function(e){
29411         if(!this.disabled && e.button == 0){
29412             this.el.addClass("x-btn-click");
29413             Roo.get(document).on('mouseup', this.onMouseUp, this);
29414         }
29415     },
29416     // private
29417     onMouseUp : function(e){
29418         if(e.button == 0){
29419             this.el.removeClass("x-btn-click");
29420             Roo.get(document).un('mouseup', this.onMouseUp, this);
29421         }
29422     },
29423     // private
29424     onMenuShow : function(e){
29425         this.el.addClass("x-btn-menu-active");
29426     },
29427     // private
29428     onMenuHide : function(e){
29429         this.el.removeClass("x-btn-menu-active");
29430     }   
29431 });
29432
29433 // Private utility class used by Button
29434 Roo.ButtonToggleMgr = function(){
29435    var groups = {};
29436    
29437    function toggleGroup(btn, state){
29438        if(state){
29439            var g = groups[btn.toggleGroup];
29440            for(var i = 0, l = g.length; i < l; i++){
29441                if(g[i] != btn){
29442                    g[i].toggle(false);
29443                }
29444            }
29445        }
29446    }
29447    
29448    return {
29449        register : function(btn){
29450            if(!btn.toggleGroup){
29451                return;
29452            }
29453            var g = groups[btn.toggleGroup];
29454            if(!g){
29455                g = groups[btn.toggleGroup] = [];
29456            }
29457            g.push(btn);
29458            btn.on("toggle", toggleGroup);
29459        },
29460        
29461        unregister : function(btn){
29462            if(!btn.toggleGroup){
29463                return;
29464            }
29465            var g = groups[btn.toggleGroup];
29466            if(g){
29467                g.remove(btn);
29468                btn.un("toggle", toggleGroup);
29469            }
29470        }
29471    };
29472 }();/*
29473  * Based on:
29474  * Ext JS Library 1.1.1
29475  * Copyright(c) 2006-2007, Ext JS, LLC.
29476  *
29477  * Originally Released Under LGPL - original licence link has changed is not relivant.
29478  *
29479  * Fork - LGPL
29480  * <script type="text/javascript">
29481  */
29482  
29483 /**
29484  * @class Roo.SplitButton
29485  * @extends Roo.Button
29486  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29487  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29488  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29489  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29490  * @cfg {String} arrowTooltip The title attribute of the arrow
29491  * @constructor
29492  * Create a new menu button
29493  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29494  * @param {Object} config The config object
29495  */
29496 Roo.SplitButton = function(renderTo, config){
29497     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29498     /**
29499      * @event arrowclick
29500      * Fires when this button's arrow is clicked
29501      * @param {SplitButton} this
29502      * @param {EventObject} e The click event
29503      */
29504     this.addEvents({"arrowclick":true});
29505 };
29506
29507 Roo.extend(Roo.SplitButton, Roo.Button, {
29508     render : function(renderTo){
29509         // this is one sweet looking template!
29510         var tpl = new Roo.Template(
29511             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29512             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29513             '<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>',
29514             "</tbody></table></td><td>",
29515             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29516             '<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>',
29517             "</tbody></table></td></tr></table>"
29518         );
29519         var btn = tpl.append(renderTo, [this.text, this.type], true);
29520         var btnEl = btn.child("button");
29521         if(this.cls){
29522             btn.addClass(this.cls);
29523         }
29524         if(this.icon){
29525             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29526         }
29527         if(this.iconCls){
29528             btnEl.addClass(this.iconCls);
29529             if(!this.cls){
29530                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29531             }
29532         }
29533         this.el = btn;
29534         if(this.handleMouseEvents){
29535             btn.on("mouseover", this.onMouseOver, this);
29536             btn.on("mouseout", this.onMouseOut, this);
29537             btn.on("mousedown", this.onMouseDown, this);
29538             btn.on("mouseup", this.onMouseUp, this);
29539         }
29540         btn.on(this.clickEvent, this.onClick, this);
29541         if(this.tooltip){
29542             if(typeof this.tooltip == 'object'){
29543                 Roo.QuickTips.tips(Roo.apply({
29544                       target: btnEl.id
29545                 }, this.tooltip));
29546             } else {
29547                 btnEl.dom[this.tooltipType] = this.tooltip;
29548             }
29549         }
29550         if(this.arrowTooltip){
29551             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29552         }
29553         if(this.hidden){
29554             this.hide();
29555         }
29556         if(this.disabled){
29557             this.disable();
29558         }
29559         if(this.pressed){
29560             this.el.addClass("x-btn-pressed");
29561         }
29562         if(Roo.isIE && !Roo.isIE7){
29563             this.autoWidth.defer(1, this);
29564         }else{
29565             this.autoWidth();
29566         }
29567         if(this.menu){
29568             this.menu.on("show", this.onMenuShow, this);
29569             this.menu.on("hide", this.onMenuHide, this);
29570         }
29571         this.fireEvent('render', this);
29572     },
29573
29574     // private
29575     autoWidth : function(){
29576         if(this.el){
29577             var tbl = this.el.child("table:first");
29578             var tbl2 = this.el.child("table:last");
29579             this.el.setWidth("auto");
29580             tbl.setWidth("auto");
29581             if(Roo.isIE7 && Roo.isStrict){
29582                 var ib = this.el.child('button:first');
29583                 if(ib && ib.getWidth() > 20){
29584                     ib.clip();
29585                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29586                 }
29587             }
29588             if(this.minWidth){
29589                 if(this.hidden){
29590                     this.el.beginMeasure();
29591                 }
29592                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29593                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29594                 }
29595                 if(this.hidden){
29596                     this.el.endMeasure();
29597                 }
29598             }
29599             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29600         } 
29601     },
29602     /**
29603      * Sets this button's click handler
29604      * @param {Function} handler The function to call when the button is clicked
29605      * @param {Object} scope (optional) Scope for the function passed above
29606      */
29607     setHandler : function(handler, scope){
29608         this.handler = handler;
29609         this.scope = scope;  
29610     },
29611     
29612     /**
29613      * Sets this button's arrow click handler
29614      * @param {Function} handler The function to call when the arrow is clicked
29615      * @param {Object} scope (optional) Scope for the function passed above
29616      */
29617     setArrowHandler : function(handler, scope){
29618         this.arrowHandler = handler;
29619         this.scope = scope;  
29620     },
29621     
29622     /**
29623      * Focus the button
29624      */
29625     focus : function(){
29626         if(this.el){
29627             this.el.child("button:first").focus();
29628         }
29629     },
29630
29631     // private
29632     onClick : function(e){
29633         e.preventDefault();
29634         if(!this.disabled){
29635             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29636                 if(this.menu && !this.menu.isVisible()){
29637                     this.menu.show(this.el, this.menuAlign);
29638                 }
29639                 this.fireEvent("arrowclick", this, e);
29640                 if(this.arrowHandler){
29641                     this.arrowHandler.call(this.scope || this, this, e);
29642                 }
29643             }else{
29644                 this.fireEvent("click", this, e);
29645                 if(this.handler){
29646                     this.handler.call(this.scope || this, this, e);
29647                 }
29648             }
29649         }
29650     },
29651     // private
29652     onMouseDown : function(e){
29653         if(!this.disabled){
29654             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29655         }
29656     },
29657     // private
29658     onMouseUp : function(e){
29659         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29660     }   
29661 });
29662
29663
29664 // backwards compat
29665 Roo.MenuButton = Roo.SplitButton;/*
29666  * Based on:
29667  * Ext JS Library 1.1.1
29668  * Copyright(c) 2006-2007, Ext JS, LLC.
29669  *
29670  * Originally Released Under LGPL - original licence link has changed is not relivant.
29671  *
29672  * Fork - LGPL
29673  * <script type="text/javascript">
29674  */
29675
29676 /**
29677  * @class Roo.Toolbar
29678  * Basic Toolbar class.
29679  * @constructor
29680  * Creates a new Toolbar
29681  * @param {Object} container The config object
29682  */ 
29683 Roo.Toolbar = function(container, buttons, config)
29684 {
29685     /// old consturctor format still supported..
29686     if(container instanceof Array){ // omit the container for later rendering
29687         buttons = container;
29688         config = buttons;
29689         container = null;
29690     }
29691     if (typeof(container) == 'object' && container.xtype) {
29692         config = container;
29693         container = config.container;
29694         buttons = config.buttons || []; // not really - use items!!
29695     }
29696     var xitems = [];
29697     if (config && config.items) {
29698         xitems = config.items;
29699         delete config.items;
29700     }
29701     Roo.apply(this, config);
29702     this.buttons = buttons;
29703     
29704     if(container){
29705         this.render(container);
29706     }
29707     this.xitems = xitems;
29708     Roo.each(xitems, function(b) {
29709         this.add(b);
29710     }, this);
29711     
29712 };
29713
29714 Roo.Toolbar.prototype = {
29715     /**
29716      * @cfg {Array} items
29717      * array of button configs or elements to add (will be converted to a MixedCollection)
29718      */
29719     
29720     /**
29721      * @cfg {String/HTMLElement/Element} container
29722      * The id or element that will contain the toolbar
29723      */
29724     // private
29725     render : function(ct){
29726         this.el = Roo.get(ct);
29727         if(this.cls){
29728             this.el.addClass(this.cls);
29729         }
29730         // using a table allows for vertical alignment
29731         // 100% width is needed by Safari...
29732         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29733         this.tr = this.el.child("tr", true);
29734         var autoId = 0;
29735         this.items = new Roo.util.MixedCollection(false, function(o){
29736             return o.id || ("item" + (++autoId));
29737         });
29738         if(this.buttons){
29739             this.add.apply(this, this.buttons);
29740             delete this.buttons;
29741         }
29742     },
29743
29744     /**
29745      * Adds element(s) to the toolbar -- this function takes a variable number of 
29746      * arguments of mixed type and adds them to the toolbar.
29747      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29748      * <ul>
29749      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29750      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29751      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29752      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29753      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29754      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29755      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29756      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29757      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29758      * </ul>
29759      * @param {Mixed} arg2
29760      * @param {Mixed} etc.
29761      */
29762     add : function(){
29763         var a = arguments, l = a.length;
29764         for(var i = 0; i < l; i++){
29765             this._add(a[i]);
29766         }
29767     },
29768     // private..
29769     _add : function(el) {
29770         
29771         if (el.xtype) {
29772             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29773         }
29774         
29775         if (el.applyTo){ // some kind of form field
29776             return this.addField(el);
29777         } 
29778         if (el.render){ // some kind of Toolbar.Item
29779             return this.addItem(el);
29780         }
29781         if (typeof el == "string"){ // string
29782             if(el == "separator" || el == "-"){
29783                 return this.addSeparator();
29784             }
29785             if (el == " "){
29786                 return this.addSpacer();
29787             }
29788             if(el == "->"){
29789                 return this.addFill();
29790             }
29791             return this.addText(el);
29792             
29793         }
29794         if(el.tagName){ // element
29795             return this.addElement(el);
29796         }
29797         if(typeof el == "object"){ // must be button config?
29798             return this.addButton(el);
29799         }
29800         // and now what?!?!
29801         return false;
29802         
29803     },
29804     
29805     /**
29806      * Add an Xtype element
29807      * @param {Object} xtype Xtype Object
29808      * @return {Object} created Object
29809      */
29810     addxtype : function(e){
29811         return this.add(e);  
29812     },
29813     
29814     /**
29815      * Returns the Element for this toolbar.
29816      * @return {Roo.Element}
29817      */
29818     getEl : function(){
29819         return this.el;  
29820     },
29821     
29822     /**
29823      * Adds a separator
29824      * @return {Roo.Toolbar.Item} The separator item
29825      */
29826     addSeparator : function(){
29827         return this.addItem(new Roo.Toolbar.Separator());
29828     },
29829
29830     /**
29831      * Adds a spacer element
29832      * @return {Roo.Toolbar.Spacer} The spacer item
29833      */
29834     addSpacer : function(){
29835         return this.addItem(new Roo.Toolbar.Spacer());
29836     },
29837
29838     /**
29839      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29840      * @return {Roo.Toolbar.Fill} The fill item
29841      */
29842     addFill : function(){
29843         return this.addItem(new Roo.Toolbar.Fill());
29844     },
29845
29846     /**
29847      * Adds any standard HTML element to the toolbar
29848      * @param {String/HTMLElement/Element} el The element or id of the element to add
29849      * @return {Roo.Toolbar.Item} The element's item
29850      */
29851     addElement : function(el){
29852         return this.addItem(new Roo.Toolbar.Item(el));
29853     },
29854     /**
29855      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29856      * @type Roo.util.MixedCollection  
29857      */
29858     items : false,
29859      
29860     /**
29861      * Adds any Toolbar.Item or subclass
29862      * @param {Roo.Toolbar.Item} item
29863      * @return {Roo.Toolbar.Item} The item
29864      */
29865     addItem : function(item){
29866         var td = this.nextBlock();
29867         item.render(td);
29868         this.items.add(item);
29869         return item;
29870     },
29871     
29872     /**
29873      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29874      * @param {Object/Array} config A button config or array of configs
29875      * @return {Roo.Toolbar.Button/Array}
29876      */
29877     addButton : function(config){
29878         if(config instanceof Array){
29879             var buttons = [];
29880             for(var i = 0, len = config.length; i < len; i++) {
29881                 buttons.push(this.addButton(config[i]));
29882             }
29883             return buttons;
29884         }
29885         var b = config;
29886         if(!(config instanceof Roo.Toolbar.Button)){
29887             b = config.split ?
29888                 new Roo.Toolbar.SplitButton(config) :
29889                 new Roo.Toolbar.Button(config);
29890         }
29891         var td = this.nextBlock();
29892         b.render(td);
29893         this.items.add(b);
29894         return b;
29895     },
29896     
29897     /**
29898      * Adds text to the toolbar
29899      * @param {String} text The text to add
29900      * @return {Roo.Toolbar.Item} The element's item
29901      */
29902     addText : function(text){
29903         return this.addItem(new Roo.Toolbar.TextItem(text));
29904     },
29905     
29906     /**
29907      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29908      * @param {Number} index The index where the item is to be inserted
29909      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29910      * @return {Roo.Toolbar.Button/Item}
29911      */
29912     insertButton : function(index, item){
29913         if(item instanceof Array){
29914             var buttons = [];
29915             for(var i = 0, len = item.length; i < len; i++) {
29916                buttons.push(this.insertButton(index + i, item[i]));
29917             }
29918             return buttons;
29919         }
29920         if (!(item instanceof Roo.Toolbar.Button)){
29921            item = new Roo.Toolbar.Button(item);
29922         }
29923         var td = document.createElement("td");
29924         this.tr.insertBefore(td, this.tr.childNodes[index]);
29925         item.render(td);
29926         this.items.insert(index, item);
29927         return item;
29928     },
29929     
29930     /**
29931      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29932      * @param {Object} config
29933      * @return {Roo.Toolbar.Item} The element's item
29934      */
29935     addDom : function(config, returnEl){
29936         var td = this.nextBlock();
29937         Roo.DomHelper.overwrite(td, config);
29938         var ti = new Roo.Toolbar.Item(td.firstChild);
29939         ti.render(td);
29940         this.items.add(ti);
29941         return ti;
29942     },
29943
29944     /**
29945      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29946      * @type Roo.util.MixedCollection  
29947      */
29948     fields : false,
29949     
29950     /**
29951      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29952      * Note: the field should not have been rendered yet. For a field that has already been
29953      * rendered, use {@link #addElement}.
29954      * @param {Roo.form.Field} field
29955      * @return {Roo.ToolbarItem}
29956      */
29957      
29958       
29959     addField : function(field) {
29960         if (!this.fields) {
29961             var autoId = 0;
29962             this.fields = new Roo.util.MixedCollection(false, function(o){
29963                 return o.id || ("item" + (++autoId));
29964             });
29965
29966         }
29967         
29968         var td = this.nextBlock();
29969         field.render(td);
29970         var ti = new Roo.Toolbar.Item(td.firstChild);
29971         ti.render(td);
29972         this.items.add(ti);
29973         this.fields.add(field);
29974         return ti;
29975     },
29976     /**
29977      * Hide the toolbar
29978      * @method hide
29979      */
29980      
29981       
29982     hide : function()
29983     {
29984         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
29985         this.el.child('div').hide();
29986     },
29987     /**
29988      * Show the toolbar
29989      * @method show
29990      */
29991     show : function()
29992     {
29993         this.el.child('div').show();
29994     },
29995       
29996     // private
29997     nextBlock : function(){
29998         var td = document.createElement("td");
29999         this.tr.appendChild(td);
30000         return td;
30001     },
30002
30003     // private
30004     destroy : function(){
30005         if(this.items){ // rendered?
30006             Roo.destroy.apply(Roo, this.items.items);
30007         }
30008         if(this.fields){ // rendered?
30009             Roo.destroy.apply(Roo, this.fields.items);
30010         }
30011         Roo.Element.uncache(this.el, this.tr);
30012     }
30013 };
30014
30015 /**
30016  * @class Roo.Toolbar.Item
30017  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30018  * @constructor
30019  * Creates a new Item
30020  * @param {HTMLElement} el 
30021  */
30022 Roo.Toolbar.Item = function(el){
30023     var cfg = {};
30024     if (typeof (el.xtype) != 'undefined') {
30025         cfg = el;
30026         el = cfg.el;
30027     }
30028     
30029     this.el = Roo.getDom(el);
30030     this.id = Roo.id(this.el);
30031     this.hidden = false;
30032     
30033     this.addEvents({
30034          /**
30035              * @event render
30036              * Fires when the button is rendered
30037              * @param {Button} this
30038              */
30039         'render': true
30040     });
30041     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30042 };
30043 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30044 //Roo.Toolbar.Item.prototype = {
30045     
30046     /**
30047      * Get this item's HTML Element
30048      * @return {HTMLElement}
30049      */
30050     getEl : function(){
30051        return this.el;  
30052     },
30053
30054     // private
30055     render : function(td){
30056         
30057          this.td = td;
30058         td.appendChild(this.el);
30059         
30060         this.fireEvent('render', this);
30061     },
30062     
30063     /**
30064      * Removes and destroys this item.
30065      */
30066     destroy : function(){
30067         this.td.parentNode.removeChild(this.td);
30068     },
30069     
30070     /**
30071      * Shows this item.
30072      */
30073     show: function(){
30074         this.hidden = false;
30075         this.td.style.display = "";
30076     },
30077     
30078     /**
30079      * Hides this item.
30080      */
30081     hide: function(){
30082         this.hidden = true;
30083         this.td.style.display = "none";
30084     },
30085     
30086     /**
30087      * Convenience function for boolean show/hide.
30088      * @param {Boolean} visible true to show/false to hide
30089      */
30090     setVisible: function(visible){
30091         if(visible) {
30092             this.show();
30093         }else{
30094             this.hide();
30095         }
30096     },
30097     
30098     /**
30099      * Try to focus this item.
30100      */
30101     focus : function(){
30102         Roo.fly(this.el).focus();
30103     },
30104     
30105     /**
30106      * Disables this item.
30107      */
30108     disable : function(){
30109         Roo.fly(this.td).addClass("x-item-disabled");
30110         this.disabled = true;
30111         this.el.disabled = true;
30112     },
30113     
30114     /**
30115      * Enables this item.
30116      */
30117     enable : function(){
30118         Roo.fly(this.td).removeClass("x-item-disabled");
30119         this.disabled = false;
30120         this.el.disabled = false;
30121     }
30122 });
30123
30124
30125 /**
30126  * @class Roo.Toolbar.Separator
30127  * @extends Roo.Toolbar.Item
30128  * A simple toolbar separator class
30129  * @constructor
30130  * Creates a new Separator
30131  */
30132 Roo.Toolbar.Separator = function(cfg){
30133     
30134     var s = document.createElement("span");
30135     s.className = "ytb-sep";
30136     if (cfg) {
30137         cfg.el = s;
30138     }
30139     
30140     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30141 };
30142 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30143     enable:Roo.emptyFn,
30144     disable:Roo.emptyFn,
30145     focus:Roo.emptyFn
30146 });
30147
30148 /**
30149  * @class Roo.Toolbar.Spacer
30150  * @extends Roo.Toolbar.Item
30151  * A simple element that adds extra horizontal space to a toolbar.
30152  * @constructor
30153  * Creates a new Spacer
30154  */
30155 Roo.Toolbar.Spacer = function(cfg){
30156     var s = document.createElement("div");
30157     s.className = "ytb-spacer";
30158     if (cfg) {
30159         cfg.el = s;
30160     }
30161     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30162 };
30163 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30164     enable:Roo.emptyFn,
30165     disable:Roo.emptyFn,
30166     focus:Roo.emptyFn
30167 });
30168
30169 /**
30170  * @class Roo.Toolbar.Fill
30171  * @extends Roo.Toolbar.Spacer
30172  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30173  * @constructor
30174  * Creates a new Spacer
30175  */
30176 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30177     // private
30178     render : function(td){
30179         td.style.width = '100%';
30180         Roo.Toolbar.Fill.superclass.render.call(this, td);
30181     }
30182 });
30183
30184 /**
30185  * @class Roo.Toolbar.TextItem
30186  * @extends Roo.Toolbar.Item
30187  * A simple class that renders text directly into a toolbar.
30188  * @constructor
30189  * Creates a new TextItem
30190  * @param {String} text
30191  */
30192 Roo.Toolbar.TextItem = function(cfg){
30193     var  text = cfg || "";
30194     if (typeof(cfg) == 'object') {
30195         text = cfg.text || "";
30196     }  else {
30197         cfg = null;
30198     }
30199     var s = document.createElement("span");
30200     s.className = "ytb-text";
30201     s.innerHTML = text;
30202     if (cfg) {
30203         cfg.el  = s;
30204     }
30205     
30206     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30207 };
30208 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30209     
30210      
30211     enable:Roo.emptyFn,
30212     disable:Roo.emptyFn,
30213     focus:Roo.emptyFn
30214 });
30215
30216 /**
30217  * @class Roo.Toolbar.Button
30218  * @extends Roo.Button
30219  * A button that renders into a toolbar.
30220  * @constructor
30221  * Creates a new Button
30222  * @param {Object} config A standard {@link Roo.Button} config object
30223  */
30224 Roo.Toolbar.Button = function(config){
30225     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30226 };
30227 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30228     render : function(td){
30229         this.td = td;
30230         Roo.Toolbar.Button.superclass.render.call(this, td);
30231     },
30232     
30233     /**
30234      * Removes and destroys this button
30235      */
30236     destroy : function(){
30237         Roo.Toolbar.Button.superclass.destroy.call(this);
30238         this.td.parentNode.removeChild(this.td);
30239     },
30240     
30241     /**
30242      * Shows this button
30243      */
30244     show: function(){
30245         this.hidden = false;
30246         this.td.style.display = "";
30247     },
30248     
30249     /**
30250      * Hides this button
30251      */
30252     hide: function(){
30253         this.hidden = true;
30254         this.td.style.display = "none";
30255     },
30256
30257     /**
30258      * Disables this item
30259      */
30260     disable : function(){
30261         Roo.fly(this.td).addClass("x-item-disabled");
30262         this.disabled = true;
30263     },
30264
30265     /**
30266      * Enables this item
30267      */
30268     enable : function(){
30269         Roo.fly(this.td).removeClass("x-item-disabled");
30270         this.disabled = false;
30271     }
30272 });
30273 // backwards compat
30274 Roo.ToolbarButton = Roo.Toolbar.Button;
30275
30276 /**
30277  * @class Roo.Toolbar.SplitButton
30278  * @extends Roo.SplitButton
30279  * A menu button that renders into a toolbar.
30280  * @constructor
30281  * Creates a new SplitButton
30282  * @param {Object} config A standard {@link Roo.SplitButton} config object
30283  */
30284 Roo.Toolbar.SplitButton = function(config){
30285     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30286 };
30287 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30288     render : function(td){
30289         this.td = td;
30290         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30291     },
30292     
30293     /**
30294      * Removes and destroys this button
30295      */
30296     destroy : function(){
30297         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30298         this.td.parentNode.removeChild(this.td);
30299     },
30300     
30301     /**
30302      * Shows this button
30303      */
30304     show: function(){
30305         this.hidden = false;
30306         this.td.style.display = "";
30307     },
30308     
30309     /**
30310      * Hides this button
30311      */
30312     hide: function(){
30313         this.hidden = true;
30314         this.td.style.display = "none";
30315     }
30316 });
30317
30318 // backwards compat
30319 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30320  * Based on:
30321  * Ext JS Library 1.1.1
30322  * Copyright(c) 2006-2007, Ext JS, LLC.
30323  *
30324  * Originally Released Under LGPL - original licence link has changed is not relivant.
30325  *
30326  * Fork - LGPL
30327  * <script type="text/javascript">
30328  */
30329  
30330 /**
30331  * @class Roo.PagingToolbar
30332  * @extends Roo.Toolbar
30333  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30334  * @constructor
30335  * Create a new PagingToolbar
30336  * @param {Object} config The config object
30337  */
30338 Roo.PagingToolbar = function(el, ds, config)
30339 {
30340     // old args format still supported... - xtype is prefered..
30341     if (typeof(el) == 'object' && el.xtype) {
30342         // created from xtype...
30343         config = el;
30344         ds = el.dataSource;
30345         el = config.container;
30346     }
30347     var items = [];
30348     if (config.items) {
30349         items = config.items;
30350         config.items = [];
30351     }
30352     
30353     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30354     this.ds = ds;
30355     this.cursor = 0;
30356     this.renderButtons(this.el);
30357     this.bind(ds);
30358     
30359     // supprot items array.
30360    
30361     Roo.each(items, function(e) {
30362         this.add(Roo.factory(e));
30363     },this);
30364     
30365 };
30366
30367 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30368     /**
30369      * @cfg {Roo.data.Store} dataSource
30370      * The underlying data store providing the paged data
30371      */
30372     /**
30373      * @cfg {String/HTMLElement/Element} container
30374      * container The id or element that will contain the toolbar
30375      */
30376     /**
30377      * @cfg {Boolean} displayInfo
30378      * True to display the displayMsg (defaults to false)
30379      */
30380     /**
30381      * @cfg {Number} pageSize
30382      * The number of records to display per page (defaults to 20)
30383      */
30384     pageSize: 20,
30385     /**
30386      * @cfg {String} displayMsg
30387      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30388      */
30389     displayMsg : 'Displaying {0} - {1} of {2}',
30390     /**
30391      * @cfg {String} emptyMsg
30392      * The message to display when no records are found (defaults to "No data to display")
30393      */
30394     emptyMsg : 'No data to display',
30395     /**
30396      * Customizable piece of the default paging text (defaults to "Page")
30397      * @type String
30398      */
30399     beforePageText : "Page",
30400     /**
30401      * Customizable piece of the default paging text (defaults to "of %0")
30402      * @type String
30403      */
30404     afterPageText : "of {0}",
30405     /**
30406      * Customizable piece of the default paging text (defaults to "First Page")
30407      * @type String
30408      */
30409     firstText : "First Page",
30410     /**
30411      * Customizable piece of the default paging text (defaults to "Previous Page")
30412      * @type String
30413      */
30414     prevText : "Previous Page",
30415     /**
30416      * Customizable piece of the default paging text (defaults to "Next Page")
30417      * @type String
30418      */
30419     nextText : "Next Page",
30420     /**
30421      * Customizable piece of the default paging text (defaults to "Last Page")
30422      * @type String
30423      */
30424     lastText : "Last Page",
30425     /**
30426      * Customizable piece of the default paging text (defaults to "Refresh")
30427      * @type String
30428      */
30429     refreshText : "Refresh",
30430
30431     // private
30432     renderButtons : function(el){
30433         Roo.PagingToolbar.superclass.render.call(this, el);
30434         this.first = this.addButton({
30435             tooltip: this.firstText,
30436             cls: "x-btn-icon x-grid-page-first",
30437             disabled: true,
30438             handler: this.onClick.createDelegate(this, ["first"])
30439         });
30440         this.prev = this.addButton({
30441             tooltip: this.prevText,
30442             cls: "x-btn-icon x-grid-page-prev",
30443             disabled: true,
30444             handler: this.onClick.createDelegate(this, ["prev"])
30445         });
30446         //this.addSeparator();
30447         this.add(this.beforePageText);
30448         this.field = Roo.get(this.addDom({
30449            tag: "input",
30450            type: "text",
30451            size: "3",
30452            value: "1",
30453            cls: "x-grid-page-number"
30454         }).el);
30455         this.field.on("keydown", this.onPagingKeydown, this);
30456         this.field.on("focus", function(){this.dom.select();});
30457         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30458         this.field.setHeight(18);
30459         //this.addSeparator();
30460         this.next = this.addButton({
30461             tooltip: this.nextText,
30462             cls: "x-btn-icon x-grid-page-next",
30463             disabled: true,
30464             handler: this.onClick.createDelegate(this, ["next"])
30465         });
30466         this.last = this.addButton({
30467             tooltip: this.lastText,
30468             cls: "x-btn-icon x-grid-page-last",
30469             disabled: true,
30470             handler: this.onClick.createDelegate(this, ["last"])
30471         });
30472         //this.addSeparator();
30473         this.loading = this.addButton({
30474             tooltip: this.refreshText,
30475             cls: "x-btn-icon x-grid-loading",
30476             handler: this.onClick.createDelegate(this, ["refresh"])
30477         });
30478
30479         if(this.displayInfo){
30480             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30481         }
30482     },
30483
30484     // private
30485     updateInfo : function(){
30486         if(this.displayEl){
30487             var count = this.ds.getCount();
30488             var msg = count == 0 ?
30489                 this.emptyMsg :
30490                 String.format(
30491                     this.displayMsg,
30492                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30493                 );
30494             this.displayEl.update(msg);
30495         }
30496     },
30497
30498     // private
30499     onLoad : function(ds, r, o){
30500        this.cursor = o.params ? o.params.start : 0;
30501        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30502
30503        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30504        this.field.dom.value = ap;
30505        this.first.setDisabled(ap == 1);
30506        this.prev.setDisabled(ap == 1);
30507        this.next.setDisabled(ap == ps);
30508        this.last.setDisabled(ap == ps);
30509        this.loading.enable();
30510        this.updateInfo();
30511     },
30512
30513     // private
30514     getPageData : function(){
30515         var total = this.ds.getTotalCount();
30516         return {
30517             total : total,
30518             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30519             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30520         };
30521     },
30522
30523     // private
30524     onLoadError : function(){
30525         this.loading.enable();
30526     },
30527
30528     // private
30529     onPagingKeydown : function(e){
30530         var k = e.getKey();
30531         var d = this.getPageData();
30532         if(k == e.RETURN){
30533             var v = this.field.dom.value, pageNum;
30534             if(!v || isNaN(pageNum = parseInt(v, 10))){
30535                 this.field.dom.value = d.activePage;
30536                 return;
30537             }
30538             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30539             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30540             e.stopEvent();
30541         }
30542         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))
30543         {
30544           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30545           this.field.dom.value = pageNum;
30546           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30547           e.stopEvent();
30548         }
30549         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30550         {
30551           var v = this.field.dom.value, pageNum; 
30552           var increment = (e.shiftKey) ? 10 : 1;
30553           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30554             increment *= -1;
30555           }
30556           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30557             this.field.dom.value = d.activePage;
30558             return;
30559           }
30560           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30561           {
30562             this.field.dom.value = parseInt(v, 10) + increment;
30563             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30564             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30565           }
30566           e.stopEvent();
30567         }
30568     },
30569
30570     // private
30571     beforeLoad : function(){
30572         if(this.loading){
30573             this.loading.disable();
30574         }
30575     },
30576
30577     // private
30578     onClick : function(which){
30579         var ds = this.ds;
30580         switch(which){
30581             case "first":
30582                 ds.load({params:{start: 0, limit: this.pageSize}});
30583             break;
30584             case "prev":
30585                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30586             break;
30587             case "next":
30588                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30589             break;
30590             case "last":
30591                 var total = ds.getTotalCount();
30592                 var extra = total % this.pageSize;
30593                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30594                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30595             break;
30596             case "refresh":
30597                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30598             break;
30599         }
30600     },
30601
30602     /**
30603      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30604      * @param {Roo.data.Store} store The data store to unbind
30605      */
30606     unbind : function(ds){
30607         ds.un("beforeload", this.beforeLoad, this);
30608         ds.un("load", this.onLoad, this);
30609         ds.un("loadexception", this.onLoadError, this);
30610         ds.un("remove", this.updateInfo, this);
30611         ds.un("add", this.updateInfo, this);
30612         this.ds = undefined;
30613     },
30614
30615     /**
30616      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30617      * @param {Roo.data.Store} store The data store to bind
30618      */
30619     bind : function(ds){
30620         ds.on("beforeload", this.beforeLoad, this);
30621         ds.on("load", this.onLoad, this);
30622         ds.on("loadexception", this.onLoadError, this);
30623         ds.on("remove", this.updateInfo, this);
30624         ds.on("add", this.updateInfo, this);
30625         this.ds = ds;
30626     }
30627 });/*
30628  * Based on:
30629  * Ext JS Library 1.1.1
30630  * Copyright(c) 2006-2007, Ext JS, LLC.
30631  *
30632  * Originally Released Under LGPL - original licence link has changed is not relivant.
30633  *
30634  * Fork - LGPL
30635  * <script type="text/javascript">
30636  */
30637
30638 /**
30639  * @class Roo.Resizable
30640  * @extends Roo.util.Observable
30641  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30642  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30643  * 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
30644  * the element will be wrapped for you automatically.</p>
30645  * <p>Here is the list of valid resize handles:</p>
30646  * <pre>
30647 Value   Description
30648 ------  -------------------
30649  'n'     north
30650  's'     south
30651  'e'     east
30652  'w'     west
30653  'nw'    northwest
30654  'sw'    southwest
30655  'se'    southeast
30656  'ne'    northeast
30657  'hd'    horizontal drag
30658  'all'   all
30659 </pre>
30660  * <p>Here's an example showing the creation of a typical Resizable:</p>
30661  * <pre><code>
30662 var resizer = new Roo.Resizable("element-id", {
30663     handles: 'all',
30664     minWidth: 200,
30665     minHeight: 100,
30666     maxWidth: 500,
30667     maxHeight: 400,
30668     pinned: true
30669 });
30670 resizer.on("resize", myHandler);
30671 </code></pre>
30672  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30673  * resizer.east.setDisplayed(false);</p>
30674  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30675  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30676  * resize operation's new size (defaults to [0, 0])
30677  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30678  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30679  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30680  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30681  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30682  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30683  * @cfg {Number} width The width of the element in pixels (defaults to null)
30684  * @cfg {Number} height The height of the element in pixels (defaults to null)
30685  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30686  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30687  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30688  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30689  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30690  * in favor of the handles config option (defaults to false)
30691  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30692  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30693  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30694  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30695  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30696  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30697  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30698  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30699  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30700  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30701  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30702  * @constructor
30703  * Create a new resizable component
30704  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30705  * @param {Object} config configuration options
30706   */
30707 Roo.Resizable = function(el, config)
30708 {
30709     this.el = Roo.get(el);
30710
30711     if(config && config.wrap){
30712         config.resizeChild = this.el;
30713         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30714         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30715         this.el.setStyle("overflow", "hidden");
30716         this.el.setPositioning(config.resizeChild.getPositioning());
30717         config.resizeChild.clearPositioning();
30718         if(!config.width || !config.height){
30719             var csize = config.resizeChild.getSize();
30720             this.el.setSize(csize.width, csize.height);
30721         }
30722         if(config.pinned && !config.adjustments){
30723             config.adjustments = "auto";
30724         }
30725     }
30726
30727     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30728     this.proxy.unselectable();
30729     this.proxy.enableDisplayMode('block');
30730
30731     Roo.apply(this, config);
30732
30733     if(this.pinned){
30734         this.disableTrackOver = true;
30735         this.el.addClass("x-resizable-pinned");
30736     }
30737     // if the element isn't positioned, make it relative
30738     var position = this.el.getStyle("position");
30739     if(position != "absolute" && position != "fixed"){
30740         this.el.setStyle("position", "relative");
30741     }
30742     if(!this.handles){ // no handles passed, must be legacy style
30743         this.handles = 's,e,se';
30744         if(this.multiDirectional){
30745             this.handles += ',n,w';
30746         }
30747     }
30748     if(this.handles == "all"){
30749         this.handles = "n s e w ne nw se sw";
30750     }
30751     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30752     var ps = Roo.Resizable.positions;
30753     for(var i = 0, len = hs.length; i < len; i++){
30754         if(hs[i] && ps[hs[i]]){
30755             var pos = ps[hs[i]];
30756             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30757         }
30758     }
30759     // legacy
30760     this.corner = this.southeast;
30761     
30762     // updateBox = the box can move..
30763     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30764         this.updateBox = true;
30765     }
30766
30767     this.activeHandle = null;
30768
30769     if(this.resizeChild){
30770         if(typeof this.resizeChild == "boolean"){
30771             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30772         }else{
30773             this.resizeChild = Roo.get(this.resizeChild, true);
30774         }
30775     }
30776     
30777     if(this.adjustments == "auto"){
30778         var rc = this.resizeChild;
30779         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30780         if(rc && (hw || hn)){
30781             rc.position("relative");
30782             rc.setLeft(hw ? hw.el.getWidth() : 0);
30783             rc.setTop(hn ? hn.el.getHeight() : 0);
30784         }
30785         this.adjustments = [
30786             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30787             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30788         ];
30789     }
30790
30791     if(this.draggable){
30792         this.dd = this.dynamic ?
30793             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30794         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30795     }
30796
30797     // public events
30798     this.addEvents({
30799         /**
30800          * @event beforeresize
30801          * Fired before resize is allowed. Set enabled to false to cancel resize.
30802          * @param {Roo.Resizable} this
30803          * @param {Roo.EventObject} e The mousedown event
30804          */
30805         "beforeresize" : true,
30806         /**
30807          * @event resizing
30808          * Fired a resizing.
30809          * @param {Roo.Resizable} this
30810          * @param {Number} x The new x position
30811          * @param {Number} y The new y position
30812          * @param {Number} w The new w width
30813          * @param {Number} h The new h hight
30814          * @param {Roo.EventObject} e The mouseup event
30815          */
30816         "resizing" : true,
30817         /**
30818          * @event resize
30819          * Fired after a resize.
30820          * @param {Roo.Resizable} this
30821          * @param {Number} width The new width
30822          * @param {Number} height The new height
30823          * @param {Roo.EventObject} e The mouseup event
30824          */
30825         "resize" : true
30826     });
30827
30828     if(this.width !== null && this.height !== null){
30829         this.resizeTo(this.width, this.height);
30830     }else{
30831         this.updateChildSize();
30832     }
30833     if(Roo.isIE){
30834         this.el.dom.style.zoom = 1;
30835     }
30836     Roo.Resizable.superclass.constructor.call(this);
30837 };
30838
30839 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30840         resizeChild : false,
30841         adjustments : [0, 0],
30842         minWidth : 5,
30843         minHeight : 5,
30844         maxWidth : 10000,
30845         maxHeight : 10000,
30846         enabled : true,
30847         animate : false,
30848         duration : .35,
30849         dynamic : false,
30850         handles : false,
30851         multiDirectional : false,
30852         disableTrackOver : false,
30853         easing : 'easeOutStrong',
30854         widthIncrement : 0,
30855         heightIncrement : 0,
30856         pinned : false,
30857         width : null,
30858         height : null,
30859         preserveRatio : false,
30860         transparent: false,
30861         minX: 0,
30862         minY: 0,
30863         draggable: false,
30864
30865         /**
30866          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30867          */
30868         constrainTo: undefined,
30869         /**
30870          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30871          */
30872         resizeRegion: undefined,
30873
30874
30875     /**
30876      * Perform a manual resize
30877      * @param {Number} width
30878      * @param {Number} height
30879      */
30880     resizeTo : function(width, height){
30881         this.el.setSize(width, height);
30882         this.updateChildSize();
30883         this.fireEvent("resize", this, width, height, null);
30884     },
30885
30886     // private
30887     startSizing : function(e, handle){
30888         this.fireEvent("beforeresize", this, e);
30889         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30890
30891             if(!this.overlay){
30892                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30893                 this.overlay.unselectable();
30894                 this.overlay.enableDisplayMode("block");
30895                 this.overlay.on("mousemove", this.onMouseMove, this);
30896                 this.overlay.on("mouseup", this.onMouseUp, this);
30897             }
30898             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30899
30900             this.resizing = true;
30901             this.startBox = this.el.getBox();
30902             this.startPoint = e.getXY();
30903             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30904                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30905
30906             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30907             this.overlay.show();
30908
30909             if(this.constrainTo) {
30910                 var ct = Roo.get(this.constrainTo);
30911                 this.resizeRegion = ct.getRegion().adjust(
30912                     ct.getFrameWidth('t'),
30913                     ct.getFrameWidth('l'),
30914                     -ct.getFrameWidth('b'),
30915                     -ct.getFrameWidth('r')
30916                 );
30917             }
30918
30919             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30920             this.proxy.show();
30921             this.proxy.setBox(this.startBox);
30922             if(!this.dynamic){
30923                 this.proxy.setStyle('visibility', 'visible');
30924             }
30925         }
30926     },
30927
30928     // private
30929     onMouseDown : function(handle, e){
30930         if(this.enabled){
30931             e.stopEvent();
30932             this.activeHandle = handle;
30933             this.startSizing(e, handle);
30934         }
30935     },
30936
30937     // private
30938     onMouseUp : function(e){
30939         var size = this.resizeElement();
30940         this.resizing = false;
30941         this.handleOut();
30942         this.overlay.hide();
30943         this.proxy.hide();
30944         this.fireEvent("resize", this, size.width, size.height, e);
30945     },
30946
30947     // private
30948     updateChildSize : function(){
30949         
30950         if(this.resizeChild){
30951             var el = this.el;
30952             var child = this.resizeChild;
30953             var adj = this.adjustments;
30954             if(el.dom.offsetWidth){
30955                 var b = el.getSize(true);
30956                 child.setSize(b.width+adj[0], b.height+adj[1]);
30957             }
30958             // Second call here for IE
30959             // The first call enables instant resizing and
30960             // the second call corrects scroll bars if they
30961             // exist
30962             if(Roo.isIE){
30963                 setTimeout(function(){
30964                     if(el.dom.offsetWidth){
30965                         var b = el.getSize(true);
30966                         child.setSize(b.width+adj[0], b.height+adj[1]);
30967                     }
30968                 }, 10);
30969             }
30970         }
30971     },
30972
30973     // private
30974     snap : function(value, inc, min){
30975         if(!inc || !value) {
30976             return value;
30977         }
30978         var newValue = value;
30979         var m = value % inc;
30980         if(m > 0){
30981             if(m > (inc/2)){
30982                 newValue = value + (inc-m);
30983             }else{
30984                 newValue = value - m;
30985             }
30986         }
30987         return Math.max(min, newValue);
30988     },
30989
30990     // private
30991     resizeElement : function(){
30992         var box = this.proxy.getBox();
30993         if(this.updateBox){
30994             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
30995         }else{
30996             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
30997         }
30998         this.updateChildSize();
30999         if(!this.dynamic){
31000             this.proxy.hide();
31001         }
31002         return box;
31003     },
31004
31005     // private
31006     constrain : function(v, diff, m, mx){
31007         if(v - diff < m){
31008             diff = v - m;
31009         }else if(v - diff > mx){
31010             diff = mx - v;
31011         }
31012         return diff;
31013     },
31014
31015     // private
31016     onMouseMove : function(e){
31017         
31018         if(this.enabled){
31019             try{// try catch so if something goes wrong the user doesn't get hung
31020
31021             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31022                 return;
31023             }
31024
31025             //var curXY = this.startPoint;
31026             var curSize = this.curSize || this.startBox;
31027             var x = this.startBox.x, y = this.startBox.y;
31028             var ox = x, oy = y;
31029             var w = curSize.width, h = curSize.height;
31030             var ow = w, oh = h;
31031             var mw = this.minWidth, mh = this.minHeight;
31032             var mxw = this.maxWidth, mxh = this.maxHeight;
31033             var wi = this.widthIncrement;
31034             var hi = this.heightIncrement;
31035
31036             var eventXY = e.getXY();
31037             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31038             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31039
31040             var pos = this.activeHandle.position;
31041
31042             switch(pos){
31043                 case "east":
31044                     w += diffX;
31045                     w = Math.min(Math.max(mw, w), mxw);
31046                     break;
31047              
31048                 case "south":
31049                     h += diffY;
31050                     h = Math.min(Math.max(mh, h), mxh);
31051                     break;
31052                 case "southeast":
31053                     w += diffX;
31054                     h += diffY;
31055                     w = Math.min(Math.max(mw, w), mxw);
31056                     h = Math.min(Math.max(mh, h), mxh);
31057                     break;
31058                 case "north":
31059                     diffY = this.constrain(h, diffY, mh, mxh);
31060                     y += diffY;
31061                     h -= diffY;
31062                     break;
31063                 case "hdrag":
31064                     
31065                     if (wi) {
31066                         var adiffX = Math.abs(diffX);
31067                         var sub = (adiffX % wi); // how much 
31068                         if (sub > (wi/2)) { // far enough to snap
31069                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31070                         } else {
31071                             // remove difference.. 
31072                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31073                         }
31074                     }
31075                     x += diffX;
31076                     x = Math.max(this.minX, x);
31077                     break;
31078                 case "west":
31079                     diffX = this.constrain(w, diffX, mw, mxw);
31080                     x += diffX;
31081                     w -= diffX;
31082                     break;
31083                 case "northeast":
31084                     w += diffX;
31085                     w = Math.min(Math.max(mw, w), mxw);
31086                     diffY = this.constrain(h, diffY, mh, mxh);
31087                     y += diffY;
31088                     h -= diffY;
31089                     break;
31090                 case "northwest":
31091                     diffX = this.constrain(w, diffX, mw, mxw);
31092                     diffY = this.constrain(h, diffY, mh, mxh);
31093                     y += diffY;
31094                     h -= diffY;
31095                     x += diffX;
31096                     w -= diffX;
31097                     break;
31098                case "southwest":
31099                     diffX = this.constrain(w, diffX, mw, mxw);
31100                     h += diffY;
31101                     h = Math.min(Math.max(mh, h), mxh);
31102                     x += diffX;
31103                     w -= diffX;
31104                     break;
31105             }
31106
31107             var sw = this.snap(w, wi, mw);
31108             var sh = this.snap(h, hi, mh);
31109             if(sw != w || sh != h){
31110                 switch(pos){
31111                     case "northeast":
31112                         y -= sh - h;
31113                     break;
31114                     case "north":
31115                         y -= sh - h;
31116                         break;
31117                     case "southwest":
31118                         x -= sw - w;
31119                     break;
31120                     case "west":
31121                         x -= sw - w;
31122                         break;
31123                     case "northwest":
31124                         x -= sw - w;
31125                         y -= sh - h;
31126                     break;
31127                 }
31128                 w = sw;
31129                 h = sh;
31130             }
31131
31132             if(this.preserveRatio){
31133                 switch(pos){
31134                     case "southeast":
31135                     case "east":
31136                         h = oh * (w/ow);
31137                         h = Math.min(Math.max(mh, h), mxh);
31138                         w = ow * (h/oh);
31139                        break;
31140                     case "south":
31141                         w = ow * (h/oh);
31142                         w = Math.min(Math.max(mw, w), mxw);
31143                         h = oh * (w/ow);
31144                         break;
31145                     case "northeast":
31146                         w = ow * (h/oh);
31147                         w = Math.min(Math.max(mw, w), mxw);
31148                         h = oh * (w/ow);
31149                     break;
31150                     case "north":
31151                         var tw = w;
31152                         w = ow * (h/oh);
31153                         w = Math.min(Math.max(mw, w), mxw);
31154                         h = oh * (w/ow);
31155                         x += (tw - w) / 2;
31156                         break;
31157                     case "southwest":
31158                         h = oh * (w/ow);
31159                         h = Math.min(Math.max(mh, h), mxh);
31160                         var tw = w;
31161                         w = ow * (h/oh);
31162                         x += tw - w;
31163                         break;
31164                     case "west":
31165                         var th = h;
31166                         h = oh * (w/ow);
31167                         h = Math.min(Math.max(mh, h), mxh);
31168                         y += (th - h) / 2;
31169                         var tw = w;
31170                         w = ow * (h/oh);
31171                         x += tw - w;
31172                        break;
31173                     case "northwest":
31174                         var tw = w;
31175                         var th = h;
31176                         h = oh * (w/ow);
31177                         h = Math.min(Math.max(mh, h), mxh);
31178                         w = ow * (h/oh);
31179                         y += th - h;
31180                         x += tw - w;
31181                        break;
31182
31183                 }
31184             }
31185             if (pos == 'hdrag') {
31186                 w = ow;
31187             }
31188             this.proxy.setBounds(x, y, w, h);
31189             if(this.dynamic){
31190                 this.resizeElement();
31191             }
31192             }catch(e){}
31193         }
31194         this.fireEvent("resizing", this, x, y, w, h, e);
31195     },
31196
31197     // private
31198     handleOver : function(){
31199         if(this.enabled){
31200             this.el.addClass("x-resizable-over");
31201         }
31202     },
31203
31204     // private
31205     handleOut : function(){
31206         if(!this.resizing){
31207             this.el.removeClass("x-resizable-over");
31208         }
31209     },
31210
31211     /**
31212      * Returns the element this component is bound to.
31213      * @return {Roo.Element}
31214      */
31215     getEl : function(){
31216         return this.el;
31217     },
31218
31219     /**
31220      * Returns the resizeChild element (or null).
31221      * @return {Roo.Element}
31222      */
31223     getResizeChild : function(){
31224         return this.resizeChild;
31225     },
31226     groupHandler : function()
31227     {
31228         
31229     },
31230     /**
31231      * Destroys this resizable. If the element was wrapped and
31232      * removeEl is not true then the element remains.
31233      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31234      */
31235     destroy : function(removeEl){
31236         this.proxy.remove();
31237         if(this.overlay){
31238             this.overlay.removeAllListeners();
31239             this.overlay.remove();
31240         }
31241         var ps = Roo.Resizable.positions;
31242         for(var k in ps){
31243             if(typeof ps[k] != "function" && this[ps[k]]){
31244                 var h = this[ps[k]];
31245                 h.el.removeAllListeners();
31246                 h.el.remove();
31247             }
31248         }
31249         if(removeEl){
31250             this.el.update("");
31251             this.el.remove();
31252         }
31253     }
31254 });
31255
31256 // private
31257 // hash to map config positions to true positions
31258 Roo.Resizable.positions = {
31259     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31260     hd: "hdrag"
31261 };
31262
31263 // private
31264 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31265     if(!this.tpl){
31266         // only initialize the template if resizable is used
31267         var tpl = Roo.DomHelper.createTemplate(
31268             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31269         );
31270         tpl.compile();
31271         Roo.Resizable.Handle.prototype.tpl = tpl;
31272     }
31273     this.position = pos;
31274     this.rz = rz;
31275     // show north drag fro topdra
31276     var handlepos = pos == 'hdrag' ? 'north' : pos;
31277     
31278     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31279     if (pos == 'hdrag') {
31280         this.el.setStyle('cursor', 'pointer');
31281     }
31282     this.el.unselectable();
31283     if(transparent){
31284         this.el.setOpacity(0);
31285     }
31286     this.el.on("mousedown", this.onMouseDown, this);
31287     if(!disableTrackOver){
31288         this.el.on("mouseover", this.onMouseOver, this);
31289         this.el.on("mouseout", this.onMouseOut, this);
31290     }
31291 };
31292
31293 // private
31294 Roo.Resizable.Handle.prototype = {
31295     afterResize : function(rz){
31296         Roo.log('after?');
31297         // do nothing
31298     },
31299     // private
31300     onMouseDown : function(e){
31301         this.rz.onMouseDown(this, e);
31302     },
31303     // private
31304     onMouseOver : function(e){
31305         this.rz.handleOver(this, e);
31306     },
31307     // private
31308     onMouseOut : function(e){
31309         this.rz.handleOut(this, e);
31310     }
31311 };/*
31312  * Based on:
31313  * Ext JS Library 1.1.1
31314  * Copyright(c) 2006-2007, Ext JS, LLC.
31315  *
31316  * Originally Released Under LGPL - original licence link has changed is not relivant.
31317  *
31318  * Fork - LGPL
31319  * <script type="text/javascript">
31320  */
31321
31322 /**
31323  * @class Roo.Editor
31324  * @extends Roo.Component
31325  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31326  * @constructor
31327  * Create a new Editor
31328  * @param {Roo.form.Field} field The Field object (or descendant)
31329  * @param {Object} config The config object
31330  */
31331 Roo.Editor = function(field, config){
31332     Roo.Editor.superclass.constructor.call(this, config);
31333     this.field = field;
31334     this.addEvents({
31335         /**
31336              * @event beforestartedit
31337              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31338              * false from the handler of this event.
31339              * @param {Editor} this
31340              * @param {Roo.Element} boundEl The underlying element bound to this editor
31341              * @param {Mixed} value The field value being set
31342              */
31343         "beforestartedit" : true,
31344         /**
31345              * @event startedit
31346              * Fires when this editor is displayed
31347              * @param {Roo.Element} boundEl The underlying element bound to this editor
31348              * @param {Mixed} value The starting field value
31349              */
31350         "startedit" : true,
31351         /**
31352              * @event beforecomplete
31353              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31354              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31355              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31356              * event will not fire since no edit actually occurred.
31357              * @param {Editor} this
31358              * @param {Mixed} value The current field value
31359              * @param {Mixed} startValue The original field value
31360              */
31361         "beforecomplete" : true,
31362         /**
31363              * @event complete
31364              * Fires after editing is complete and any changed value has been written to the underlying field.
31365              * @param {Editor} this
31366              * @param {Mixed} value The current field value
31367              * @param {Mixed} startValue The original field value
31368              */
31369         "complete" : true,
31370         /**
31371          * @event specialkey
31372          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31373          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31374          * @param {Roo.form.Field} this
31375          * @param {Roo.EventObject} e The event object
31376          */
31377         "specialkey" : true
31378     });
31379 };
31380
31381 Roo.extend(Roo.Editor, Roo.Component, {
31382     /**
31383      * @cfg {Boolean/String} autosize
31384      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31385      * or "height" to adopt the height only (defaults to false)
31386      */
31387     /**
31388      * @cfg {Boolean} revertInvalid
31389      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31390      * validation fails (defaults to true)
31391      */
31392     /**
31393      * @cfg {Boolean} ignoreNoChange
31394      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31395      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31396      * will never be ignored.
31397      */
31398     /**
31399      * @cfg {Boolean} hideEl
31400      * False to keep the bound element visible while the editor is displayed (defaults to true)
31401      */
31402     /**
31403      * @cfg {Mixed} value
31404      * The data value of the underlying field (defaults to "")
31405      */
31406     value : "",
31407     /**
31408      * @cfg {String} alignment
31409      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31410      */
31411     alignment: "c-c?",
31412     /**
31413      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31414      * for bottom-right shadow (defaults to "frame")
31415      */
31416     shadow : "frame",
31417     /**
31418      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31419      */
31420     constrain : false,
31421     /**
31422      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31423      */
31424     completeOnEnter : false,
31425     /**
31426      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31427      */
31428     cancelOnEsc : false,
31429     /**
31430      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31431      */
31432     updateEl : false,
31433
31434     // private
31435     onRender : function(ct, position){
31436         this.el = new Roo.Layer({
31437             shadow: this.shadow,
31438             cls: "x-editor",
31439             parentEl : ct,
31440             shim : this.shim,
31441             shadowOffset:4,
31442             id: this.id,
31443             constrain: this.constrain
31444         });
31445         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31446         if(this.field.msgTarget != 'title'){
31447             this.field.msgTarget = 'qtip';
31448         }
31449         this.field.render(this.el);
31450         if(Roo.isGecko){
31451             this.field.el.dom.setAttribute('autocomplete', 'off');
31452         }
31453         this.field.on("specialkey", this.onSpecialKey, this);
31454         if(this.swallowKeys){
31455             this.field.el.swallowEvent(['keydown','keypress']);
31456         }
31457         this.field.show();
31458         this.field.on("blur", this.onBlur, this);
31459         if(this.field.grow){
31460             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31461         }
31462     },
31463
31464     onSpecialKey : function(field, e)
31465     {
31466         //Roo.log('editor onSpecialKey');
31467         if(this.completeOnEnter && e.getKey() == e.ENTER){
31468             e.stopEvent();
31469             this.completeEdit();
31470             return;
31471         }
31472         // do not fire special key otherwise it might hide close the editor...
31473         if(e.getKey() == e.ENTER){    
31474             return;
31475         }
31476         if(this.cancelOnEsc && e.getKey() == e.ESC){
31477             this.cancelEdit();
31478             return;
31479         } 
31480         this.fireEvent('specialkey', field, e);
31481     
31482     },
31483
31484     /**
31485      * Starts the editing process and shows the editor.
31486      * @param {String/HTMLElement/Element} el The element to edit
31487      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31488       * to the innerHTML of el.
31489      */
31490     startEdit : function(el, value){
31491         if(this.editing){
31492             this.completeEdit();
31493         }
31494         this.boundEl = Roo.get(el);
31495         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31496         if(!this.rendered){
31497             this.render(this.parentEl || document.body);
31498         }
31499         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31500             return;
31501         }
31502         this.startValue = v;
31503         this.field.setValue(v);
31504         if(this.autoSize){
31505             var sz = this.boundEl.getSize();
31506             switch(this.autoSize){
31507                 case "width":
31508                 this.setSize(sz.width,  "");
31509                 break;
31510                 case "height":
31511                 this.setSize("",  sz.height);
31512                 break;
31513                 default:
31514                 this.setSize(sz.width,  sz.height);
31515             }
31516         }
31517         this.el.alignTo(this.boundEl, this.alignment);
31518         this.editing = true;
31519         if(Roo.QuickTips){
31520             Roo.QuickTips.disable();
31521         }
31522         this.show();
31523     },
31524
31525     /**
31526      * Sets the height and width of this editor.
31527      * @param {Number} width The new width
31528      * @param {Number} height The new height
31529      */
31530     setSize : function(w, h){
31531         this.field.setSize(w, h);
31532         if(this.el){
31533             this.el.sync();
31534         }
31535     },
31536
31537     /**
31538      * Realigns the editor to the bound field based on the current alignment config value.
31539      */
31540     realign : function(){
31541         this.el.alignTo(this.boundEl, this.alignment);
31542     },
31543
31544     /**
31545      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31546      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31547      */
31548     completeEdit : function(remainVisible){
31549         if(!this.editing){
31550             return;
31551         }
31552         var v = this.getValue();
31553         if(this.revertInvalid !== false && !this.field.isValid()){
31554             v = this.startValue;
31555             this.cancelEdit(true);
31556         }
31557         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31558             this.editing = false;
31559             this.hide();
31560             return;
31561         }
31562         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31563             this.editing = false;
31564             if(this.updateEl && this.boundEl){
31565                 this.boundEl.update(v);
31566             }
31567             if(remainVisible !== true){
31568                 this.hide();
31569             }
31570             this.fireEvent("complete", this, v, this.startValue);
31571         }
31572     },
31573
31574     // private
31575     onShow : function(){
31576         this.el.show();
31577         if(this.hideEl !== false){
31578             this.boundEl.hide();
31579         }
31580         this.field.show();
31581         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31582             this.fixIEFocus = true;
31583             this.deferredFocus.defer(50, this);
31584         }else{
31585             this.field.focus();
31586         }
31587         this.fireEvent("startedit", this.boundEl, this.startValue);
31588     },
31589
31590     deferredFocus : function(){
31591         if(this.editing){
31592             this.field.focus();
31593         }
31594     },
31595
31596     /**
31597      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31598      * reverted to the original starting value.
31599      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31600      * cancel (defaults to false)
31601      */
31602     cancelEdit : function(remainVisible){
31603         if(this.editing){
31604             this.setValue(this.startValue);
31605             if(remainVisible !== true){
31606                 this.hide();
31607             }
31608         }
31609     },
31610
31611     // private
31612     onBlur : function(){
31613         if(this.allowBlur !== true && this.editing){
31614             this.completeEdit();
31615         }
31616     },
31617
31618     // private
31619     onHide : function(){
31620         if(this.editing){
31621             this.completeEdit();
31622             return;
31623         }
31624         this.field.blur();
31625         if(this.field.collapse){
31626             this.field.collapse();
31627         }
31628         this.el.hide();
31629         if(this.hideEl !== false){
31630             this.boundEl.show();
31631         }
31632         if(Roo.QuickTips){
31633             Roo.QuickTips.enable();
31634         }
31635     },
31636
31637     /**
31638      * Sets the data value of the editor
31639      * @param {Mixed} value Any valid value supported by the underlying field
31640      */
31641     setValue : function(v){
31642         this.field.setValue(v);
31643     },
31644
31645     /**
31646      * Gets the data value of the editor
31647      * @return {Mixed} The data value
31648      */
31649     getValue : function(){
31650         return this.field.getValue();
31651     }
31652 });/*
31653  * Based on:
31654  * Ext JS Library 1.1.1
31655  * Copyright(c) 2006-2007, Ext JS, LLC.
31656  *
31657  * Originally Released Under LGPL - original licence link has changed is not relivant.
31658  *
31659  * Fork - LGPL
31660  * <script type="text/javascript">
31661  */
31662  
31663 /**
31664  * @class Roo.BasicDialog
31665  * @extends Roo.util.Observable
31666  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31667  * <pre><code>
31668 var dlg = new Roo.BasicDialog("my-dlg", {
31669     height: 200,
31670     width: 300,
31671     minHeight: 100,
31672     minWidth: 150,
31673     modal: true,
31674     proxyDrag: true,
31675     shadow: true
31676 });
31677 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31678 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31679 dlg.addButton('Cancel', dlg.hide, dlg);
31680 dlg.show();
31681 </code></pre>
31682   <b>A Dialog should always be a direct child of the body element.</b>
31683  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31684  * @cfg {String} title Default text to display in the title bar (defaults to null)
31685  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31686  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31687  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31688  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31689  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31690  * (defaults to null with no animation)
31691  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31692  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31693  * property for valid values (defaults to 'all')
31694  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31695  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31696  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31697  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31698  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31699  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31700  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31701  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31702  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31703  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31704  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31705  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31706  * draggable = true (defaults to false)
31707  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31708  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31709  * shadow (defaults to false)
31710  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31711  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31712  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31713  * @cfg {Array} buttons Array of buttons
31714  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31715  * @constructor
31716  * Create a new BasicDialog.
31717  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31718  * @param {Object} config Configuration options
31719  */
31720 Roo.BasicDialog = function(el, config){
31721     this.el = Roo.get(el);
31722     var dh = Roo.DomHelper;
31723     if(!this.el && config && config.autoCreate){
31724         if(typeof config.autoCreate == "object"){
31725             if(!config.autoCreate.id){
31726                 config.autoCreate.id = el;
31727             }
31728             this.el = dh.append(document.body,
31729                         config.autoCreate, true);
31730         }else{
31731             this.el = dh.append(document.body,
31732                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31733         }
31734     }
31735     el = this.el;
31736     el.setDisplayed(true);
31737     el.hide = this.hideAction;
31738     this.id = el.id;
31739     el.addClass("x-dlg");
31740
31741     Roo.apply(this, config);
31742
31743     this.proxy = el.createProxy("x-dlg-proxy");
31744     this.proxy.hide = this.hideAction;
31745     this.proxy.setOpacity(.5);
31746     this.proxy.hide();
31747
31748     if(config.width){
31749         el.setWidth(config.width);
31750     }
31751     if(config.height){
31752         el.setHeight(config.height);
31753     }
31754     this.size = el.getSize();
31755     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31756         this.xy = [config.x,config.y];
31757     }else{
31758         this.xy = el.getCenterXY(true);
31759     }
31760     /** The header element @type Roo.Element */
31761     this.header = el.child("> .x-dlg-hd");
31762     /** The body element @type Roo.Element */
31763     this.body = el.child("> .x-dlg-bd");
31764     /** The footer element @type Roo.Element */
31765     this.footer = el.child("> .x-dlg-ft");
31766
31767     if(!this.header){
31768         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31769     }
31770     if(!this.body){
31771         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31772     }
31773
31774     this.header.unselectable();
31775     if(this.title){
31776         this.header.update(this.title);
31777     }
31778     // this element allows the dialog to be focused for keyboard event
31779     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31780     this.focusEl.swallowEvent("click", true);
31781
31782     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31783
31784     // wrap the body and footer for special rendering
31785     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31786     if(this.footer){
31787         this.bwrap.dom.appendChild(this.footer.dom);
31788     }
31789
31790     this.bg = this.el.createChild({
31791         tag: "div", cls:"x-dlg-bg",
31792         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31793     });
31794     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31795
31796
31797     if(this.autoScroll !== false && !this.autoTabs){
31798         this.body.setStyle("overflow", "auto");
31799     }
31800
31801     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31802
31803     if(this.closable !== false){
31804         this.el.addClass("x-dlg-closable");
31805         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31806         this.close.on("click", this.closeClick, this);
31807         this.close.addClassOnOver("x-dlg-close-over");
31808     }
31809     if(this.collapsible !== false){
31810         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31811         this.collapseBtn.on("click", this.collapseClick, this);
31812         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31813         this.header.on("dblclick", this.collapseClick, this);
31814     }
31815     if(this.resizable !== false){
31816         this.el.addClass("x-dlg-resizable");
31817         this.resizer = new Roo.Resizable(el, {
31818             minWidth: this.minWidth || 80,
31819             minHeight:this.minHeight || 80,
31820             handles: this.resizeHandles || "all",
31821             pinned: true
31822         });
31823         this.resizer.on("beforeresize", this.beforeResize, this);
31824         this.resizer.on("resize", this.onResize, this);
31825     }
31826     if(this.draggable !== false){
31827         el.addClass("x-dlg-draggable");
31828         if (!this.proxyDrag) {
31829             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31830         }
31831         else {
31832             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31833         }
31834         dd.setHandleElId(this.header.id);
31835         dd.endDrag = this.endMove.createDelegate(this);
31836         dd.startDrag = this.startMove.createDelegate(this);
31837         dd.onDrag = this.onDrag.createDelegate(this);
31838         dd.scroll = false;
31839         this.dd = dd;
31840     }
31841     if(this.modal){
31842         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31843         this.mask.enableDisplayMode("block");
31844         this.mask.hide();
31845         this.el.addClass("x-dlg-modal");
31846     }
31847     if(this.shadow){
31848         this.shadow = new Roo.Shadow({
31849             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31850             offset : this.shadowOffset
31851         });
31852     }else{
31853         this.shadowOffset = 0;
31854     }
31855     if(Roo.useShims && this.shim !== false){
31856         this.shim = this.el.createShim();
31857         this.shim.hide = this.hideAction;
31858         this.shim.hide();
31859     }else{
31860         this.shim = false;
31861     }
31862     if(this.autoTabs){
31863         this.initTabs();
31864     }
31865     if (this.buttons) { 
31866         var bts= this.buttons;
31867         this.buttons = [];
31868         Roo.each(bts, function(b) {
31869             this.addButton(b);
31870         }, this);
31871     }
31872     
31873     
31874     this.addEvents({
31875         /**
31876          * @event keydown
31877          * Fires when a key is pressed
31878          * @param {Roo.BasicDialog} this
31879          * @param {Roo.EventObject} e
31880          */
31881         "keydown" : true,
31882         /**
31883          * @event move
31884          * Fires when this dialog is moved by the user.
31885          * @param {Roo.BasicDialog} this
31886          * @param {Number} x The new page X
31887          * @param {Number} y The new page Y
31888          */
31889         "move" : true,
31890         /**
31891          * @event resize
31892          * Fires when this dialog is resized by the user.
31893          * @param {Roo.BasicDialog} this
31894          * @param {Number} width The new width
31895          * @param {Number} height The new height
31896          */
31897         "resize" : true,
31898         /**
31899          * @event beforehide
31900          * Fires before this dialog is hidden.
31901          * @param {Roo.BasicDialog} this
31902          */
31903         "beforehide" : true,
31904         /**
31905          * @event hide
31906          * Fires when this dialog is hidden.
31907          * @param {Roo.BasicDialog} this
31908          */
31909         "hide" : true,
31910         /**
31911          * @event beforeshow
31912          * Fires before this dialog is shown.
31913          * @param {Roo.BasicDialog} this
31914          */
31915         "beforeshow" : true,
31916         /**
31917          * @event show
31918          * Fires when this dialog is shown.
31919          * @param {Roo.BasicDialog} this
31920          */
31921         "show" : true
31922     });
31923     el.on("keydown", this.onKeyDown, this);
31924     el.on("mousedown", this.toFront, this);
31925     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31926     this.el.hide();
31927     Roo.DialogManager.register(this);
31928     Roo.BasicDialog.superclass.constructor.call(this);
31929 };
31930
31931 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31932     shadowOffset: Roo.isIE ? 6 : 5,
31933     minHeight: 80,
31934     minWidth: 200,
31935     minButtonWidth: 75,
31936     defaultButton: null,
31937     buttonAlign: "right",
31938     tabTag: 'div',
31939     firstShow: true,
31940
31941     /**
31942      * Sets the dialog title text
31943      * @param {String} text The title text to display
31944      * @return {Roo.BasicDialog} this
31945      */
31946     setTitle : function(text){
31947         this.header.update(text);
31948         return this;
31949     },
31950
31951     // private
31952     closeClick : function(){
31953         this.hide();
31954     },
31955
31956     // private
31957     collapseClick : function(){
31958         this[this.collapsed ? "expand" : "collapse"]();
31959     },
31960
31961     /**
31962      * Collapses the dialog to its minimized state (only the title bar is visible).
31963      * Equivalent to the user clicking the collapse dialog button.
31964      */
31965     collapse : function(){
31966         if(!this.collapsed){
31967             this.collapsed = true;
31968             this.el.addClass("x-dlg-collapsed");
31969             this.restoreHeight = this.el.getHeight();
31970             this.resizeTo(this.el.getWidth(), this.header.getHeight());
31971         }
31972     },
31973
31974     /**
31975      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
31976      * clicking the expand dialog button.
31977      */
31978     expand : function(){
31979         if(this.collapsed){
31980             this.collapsed = false;
31981             this.el.removeClass("x-dlg-collapsed");
31982             this.resizeTo(this.el.getWidth(), this.restoreHeight);
31983         }
31984     },
31985
31986     /**
31987      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
31988      * @return {Roo.TabPanel} The tabs component
31989      */
31990     initTabs : function(){
31991         var tabs = this.getTabs();
31992         while(tabs.getTab(0)){
31993             tabs.removeTab(0);
31994         }
31995         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
31996             var dom = el.dom;
31997             tabs.addTab(Roo.id(dom), dom.title);
31998             dom.title = "";
31999         });
32000         tabs.activate(0);
32001         return tabs;
32002     },
32003
32004     // private
32005     beforeResize : function(){
32006         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32007     },
32008
32009     // private
32010     onResize : function(){
32011         this.refreshSize();
32012         this.syncBodyHeight();
32013         this.adjustAssets();
32014         this.focus();
32015         this.fireEvent("resize", this, this.size.width, this.size.height);
32016     },
32017
32018     // private
32019     onKeyDown : function(e){
32020         if(this.isVisible()){
32021             this.fireEvent("keydown", this, e);
32022         }
32023     },
32024
32025     /**
32026      * Resizes the dialog.
32027      * @param {Number} width
32028      * @param {Number} height
32029      * @return {Roo.BasicDialog} this
32030      */
32031     resizeTo : function(width, height){
32032         this.el.setSize(width, height);
32033         this.size = {width: width, height: height};
32034         this.syncBodyHeight();
32035         if(this.fixedcenter){
32036             this.center();
32037         }
32038         if(this.isVisible()){
32039             this.constrainXY();
32040             this.adjustAssets();
32041         }
32042         this.fireEvent("resize", this, width, height);
32043         return this;
32044     },
32045
32046
32047     /**
32048      * Resizes the dialog to fit the specified content size.
32049      * @param {Number} width
32050      * @param {Number} height
32051      * @return {Roo.BasicDialog} this
32052      */
32053     setContentSize : function(w, h){
32054         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32055         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32056         //if(!this.el.isBorderBox()){
32057             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32058             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32059         //}
32060         if(this.tabs){
32061             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32062             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32063         }
32064         this.resizeTo(w, h);
32065         return this;
32066     },
32067
32068     /**
32069      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32070      * executed in response to a particular key being pressed while the dialog is active.
32071      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32072      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32073      * @param {Function} fn The function to call
32074      * @param {Object} scope (optional) The scope of the function
32075      * @return {Roo.BasicDialog} this
32076      */
32077     addKeyListener : function(key, fn, scope){
32078         var keyCode, shift, ctrl, alt;
32079         if(typeof key == "object" && !(key instanceof Array)){
32080             keyCode = key["key"];
32081             shift = key["shift"];
32082             ctrl = key["ctrl"];
32083             alt = key["alt"];
32084         }else{
32085             keyCode = key;
32086         }
32087         var handler = function(dlg, e){
32088             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32089                 var k = e.getKey();
32090                 if(keyCode instanceof Array){
32091                     for(var i = 0, len = keyCode.length; i < len; i++){
32092                         if(keyCode[i] == k){
32093                           fn.call(scope || window, dlg, k, e);
32094                           return;
32095                         }
32096                     }
32097                 }else{
32098                     if(k == keyCode){
32099                         fn.call(scope || window, dlg, k, e);
32100                     }
32101                 }
32102             }
32103         };
32104         this.on("keydown", handler);
32105         return this;
32106     },
32107
32108     /**
32109      * Returns the TabPanel component (creates it if it doesn't exist).
32110      * Note: If you wish to simply check for the existence of tabs without creating them,
32111      * check for a null 'tabs' property.
32112      * @return {Roo.TabPanel} The tabs component
32113      */
32114     getTabs : function(){
32115         if(!this.tabs){
32116             this.el.addClass("x-dlg-auto-tabs");
32117             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32118             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32119         }
32120         return this.tabs;
32121     },
32122
32123     /**
32124      * Adds a button to the footer section of the dialog.
32125      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32126      * object or a valid Roo.DomHelper element config
32127      * @param {Function} handler The function called when the button is clicked
32128      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32129      * @return {Roo.Button} The new button
32130      */
32131     addButton : function(config, handler, scope){
32132         var dh = Roo.DomHelper;
32133         if(!this.footer){
32134             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32135         }
32136         if(!this.btnContainer){
32137             var tb = this.footer.createChild({
32138
32139                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32140                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32141             }, null, true);
32142             this.btnContainer = tb.firstChild.firstChild.firstChild;
32143         }
32144         var bconfig = {
32145             handler: handler,
32146             scope: scope,
32147             minWidth: this.minButtonWidth,
32148             hideParent:true
32149         };
32150         if(typeof config == "string"){
32151             bconfig.text = config;
32152         }else{
32153             if(config.tag){
32154                 bconfig.dhconfig = config;
32155             }else{
32156                 Roo.apply(bconfig, config);
32157             }
32158         }
32159         var fc = false;
32160         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32161             bconfig.position = Math.max(0, bconfig.position);
32162             fc = this.btnContainer.childNodes[bconfig.position];
32163         }
32164          
32165         var btn = new Roo.Button(
32166             fc ? 
32167                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32168                 : this.btnContainer.appendChild(document.createElement("td")),
32169             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32170             bconfig
32171         );
32172         this.syncBodyHeight();
32173         if(!this.buttons){
32174             /**
32175              * Array of all the buttons that have been added to this dialog via addButton
32176              * @type Array
32177              */
32178             this.buttons = [];
32179         }
32180         this.buttons.push(btn);
32181         return btn;
32182     },
32183
32184     /**
32185      * Sets the default button to be focused when the dialog is displayed.
32186      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32187      * @return {Roo.BasicDialog} this
32188      */
32189     setDefaultButton : function(btn){
32190         this.defaultButton = btn;
32191         return this;
32192     },
32193
32194     // private
32195     getHeaderFooterHeight : function(safe){
32196         var height = 0;
32197         if(this.header){
32198            height += this.header.getHeight();
32199         }
32200         if(this.footer){
32201            var fm = this.footer.getMargins();
32202             height += (this.footer.getHeight()+fm.top+fm.bottom);
32203         }
32204         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32205         height += this.centerBg.getPadding("tb");
32206         return height;
32207     },
32208
32209     // private
32210     syncBodyHeight : function()
32211     {
32212         var bd = this.body, // the text
32213             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32214             bw = this.bwrap;
32215         var height = this.size.height - this.getHeaderFooterHeight(false);
32216         bd.setHeight(height-bd.getMargins("tb"));
32217         var hh = this.header.getHeight();
32218         var h = this.size.height-hh;
32219         cb.setHeight(h);
32220         
32221         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32222         bw.setHeight(h-cb.getPadding("tb"));
32223         
32224         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32225         bd.setWidth(bw.getWidth(true));
32226         if(this.tabs){
32227             this.tabs.syncHeight();
32228             if(Roo.isIE){
32229                 this.tabs.el.repaint();
32230             }
32231         }
32232     },
32233
32234     /**
32235      * Restores the previous state of the dialog if Roo.state is configured.
32236      * @return {Roo.BasicDialog} this
32237      */
32238     restoreState : function(){
32239         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32240         if(box && box.width){
32241             this.xy = [box.x, box.y];
32242             this.resizeTo(box.width, box.height);
32243         }
32244         return this;
32245     },
32246
32247     // private
32248     beforeShow : function(){
32249         this.expand();
32250         if(this.fixedcenter){
32251             this.xy = this.el.getCenterXY(true);
32252         }
32253         if(this.modal){
32254             Roo.get(document.body).addClass("x-body-masked");
32255             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32256             this.mask.show();
32257         }
32258         this.constrainXY();
32259     },
32260
32261     // private
32262     animShow : function(){
32263         var b = Roo.get(this.animateTarget).getBox();
32264         this.proxy.setSize(b.width, b.height);
32265         this.proxy.setLocation(b.x, b.y);
32266         this.proxy.show();
32267         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32268                     true, .35, this.showEl.createDelegate(this));
32269     },
32270
32271     /**
32272      * Shows the dialog.
32273      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32274      * @return {Roo.BasicDialog} this
32275      */
32276     show : function(animateTarget){
32277         if (this.fireEvent("beforeshow", this) === false){
32278             return;
32279         }
32280         if(this.syncHeightBeforeShow){
32281             this.syncBodyHeight();
32282         }else if(this.firstShow){
32283             this.firstShow = false;
32284             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32285         }
32286         this.animateTarget = animateTarget || this.animateTarget;
32287         if(!this.el.isVisible()){
32288             this.beforeShow();
32289             if(this.animateTarget && Roo.get(this.animateTarget)){
32290                 this.animShow();
32291             }else{
32292                 this.showEl();
32293             }
32294         }
32295         return this;
32296     },
32297
32298     // private
32299     showEl : function(){
32300         this.proxy.hide();
32301         this.el.setXY(this.xy);
32302         this.el.show();
32303         this.adjustAssets(true);
32304         this.toFront();
32305         this.focus();
32306         // IE peekaboo bug - fix found by Dave Fenwick
32307         if(Roo.isIE){
32308             this.el.repaint();
32309         }
32310         this.fireEvent("show", this);
32311     },
32312
32313     /**
32314      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32315      * dialog itself will receive focus.
32316      */
32317     focus : function(){
32318         if(this.defaultButton){
32319             this.defaultButton.focus();
32320         }else{
32321             this.focusEl.focus();
32322         }
32323     },
32324
32325     // private
32326     constrainXY : function(){
32327         if(this.constraintoviewport !== false){
32328             if(!this.viewSize){
32329                 if(this.container){
32330                     var s = this.container.getSize();
32331                     this.viewSize = [s.width, s.height];
32332                 }else{
32333                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32334                 }
32335             }
32336             var s = Roo.get(this.container||document).getScroll();
32337
32338             var x = this.xy[0], y = this.xy[1];
32339             var w = this.size.width, h = this.size.height;
32340             var vw = this.viewSize[0], vh = this.viewSize[1];
32341             // only move it if it needs it
32342             var moved = false;
32343             // first validate right/bottom
32344             if(x + w > vw+s.left){
32345                 x = vw - w;
32346                 moved = true;
32347             }
32348             if(y + h > vh+s.top){
32349                 y = vh - h;
32350                 moved = true;
32351             }
32352             // then make sure top/left isn't negative
32353             if(x < s.left){
32354                 x = s.left;
32355                 moved = true;
32356             }
32357             if(y < s.top){
32358                 y = s.top;
32359                 moved = true;
32360             }
32361             if(moved){
32362                 // cache xy
32363                 this.xy = [x, y];
32364                 if(this.isVisible()){
32365                     this.el.setLocation(x, y);
32366                     this.adjustAssets();
32367                 }
32368             }
32369         }
32370     },
32371
32372     // private
32373     onDrag : function(){
32374         if(!this.proxyDrag){
32375             this.xy = this.el.getXY();
32376             this.adjustAssets();
32377         }
32378     },
32379
32380     // private
32381     adjustAssets : function(doShow){
32382         var x = this.xy[0], y = this.xy[1];
32383         var w = this.size.width, h = this.size.height;
32384         if(doShow === true){
32385             if(this.shadow){
32386                 this.shadow.show(this.el);
32387             }
32388             if(this.shim){
32389                 this.shim.show();
32390             }
32391         }
32392         if(this.shadow && this.shadow.isVisible()){
32393             this.shadow.show(this.el);
32394         }
32395         if(this.shim && this.shim.isVisible()){
32396             this.shim.setBounds(x, y, w, h);
32397         }
32398     },
32399
32400     // private
32401     adjustViewport : function(w, h){
32402         if(!w || !h){
32403             w = Roo.lib.Dom.getViewWidth();
32404             h = Roo.lib.Dom.getViewHeight();
32405         }
32406         // cache the size
32407         this.viewSize = [w, h];
32408         if(this.modal && this.mask.isVisible()){
32409             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32410             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32411         }
32412         if(this.isVisible()){
32413             this.constrainXY();
32414         }
32415     },
32416
32417     /**
32418      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32419      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32420      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32421      */
32422     destroy : function(removeEl){
32423         if(this.isVisible()){
32424             this.animateTarget = null;
32425             this.hide();
32426         }
32427         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32428         if(this.tabs){
32429             this.tabs.destroy(removeEl);
32430         }
32431         Roo.destroy(
32432              this.shim,
32433              this.proxy,
32434              this.resizer,
32435              this.close,
32436              this.mask
32437         );
32438         if(this.dd){
32439             this.dd.unreg();
32440         }
32441         if(this.buttons){
32442            for(var i = 0, len = this.buttons.length; i < len; i++){
32443                this.buttons[i].destroy();
32444            }
32445         }
32446         this.el.removeAllListeners();
32447         if(removeEl === true){
32448             this.el.update("");
32449             this.el.remove();
32450         }
32451         Roo.DialogManager.unregister(this);
32452     },
32453
32454     // private
32455     startMove : function(){
32456         if(this.proxyDrag){
32457             this.proxy.show();
32458         }
32459         if(this.constraintoviewport !== false){
32460             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32461         }
32462     },
32463
32464     // private
32465     endMove : function(){
32466         if(!this.proxyDrag){
32467             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32468         }else{
32469             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32470             this.proxy.hide();
32471         }
32472         this.refreshSize();
32473         this.adjustAssets();
32474         this.focus();
32475         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32476     },
32477
32478     /**
32479      * Brings this dialog to the front of any other visible dialogs
32480      * @return {Roo.BasicDialog} this
32481      */
32482     toFront : function(){
32483         Roo.DialogManager.bringToFront(this);
32484         return this;
32485     },
32486
32487     /**
32488      * Sends this dialog to the back (under) of any other visible dialogs
32489      * @return {Roo.BasicDialog} this
32490      */
32491     toBack : function(){
32492         Roo.DialogManager.sendToBack(this);
32493         return this;
32494     },
32495
32496     /**
32497      * Centers this dialog in the viewport
32498      * @return {Roo.BasicDialog} this
32499      */
32500     center : function(){
32501         var xy = this.el.getCenterXY(true);
32502         this.moveTo(xy[0], xy[1]);
32503         return this;
32504     },
32505
32506     /**
32507      * Moves the dialog's top-left corner to the specified point
32508      * @param {Number} x
32509      * @param {Number} y
32510      * @return {Roo.BasicDialog} this
32511      */
32512     moveTo : function(x, y){
32513         this.xy = [x,y];
32514         if(this.isVisible()){
32515             this.el.setXY(this.xy);
32516             this.adjustAssets();
32517         }
32518         return this;
32519     },
32520
32521     /**
32522      * Aligns the dialog to the specified element
32523      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32524      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32525      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32526      * @return {Roo.BasicDialog} this
32527      */
32528     alignTo : function(element, position, offsets){
32529         this.xy = this.el.getAlignToXY(element, position, offsets);
32530         if(this.isVisible()){
32531             this.el.setXY(this.xy);
32532             this.adjustAssets();
32533         }
32534         return this;
32535     },
32536
32537     /**
32538      * Anchors an element to another element and realigns it when the window is resized.
32539      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32540      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32541      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32542      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32543      * is a number, it is used as the buffer delay (defaults to 50ms).
32544      * @return {Roo.BasicDialog} this
32545      */
32546     anchorTo : function(el, alignment, offsets, monitorScroll){
32547         var action = function(){
32548             this.alignTo(el, alignment, offsets);
32549         };
32550         Roo.EventManager.onWindowResize(action, this);
32551         var tm = typeof monitorScroll;
32552         if(tm != 'undefined'){
32553             Roo.EventManager.on(window, 'scroll', action, this,
32554                 {buffer: tm == 'number' ? monitorScroll : 50});
32555         }
32556         action.call(this);
32557         return this;
32558     },
32559
32560     /**
32561      * Returns true if the dialog is visible
32562      * @return {Boolean}
32563      */
32564     isVisible : function(){
32565         return this.el.isVisible();
32566     },
32567
32568     // private
32569     animHide : function(callback){
32570         var b = Roo.get(this.animateTarget).getBox();
32571         this.proxy.show();
32572         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32573         this.el.hide();
32574         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32575                     this.hideEl.createDelegate(this, [callback]));
32576     },
32577
32578     /**
32579      * Hides the dialog.
32580      * @param {Function} callback (optional) Function to call when the dialog is hidden
32581      * @return {Roo.BasicDialog} this
32582      */
32583     hide : function(callback){
32584         if (this.fireEvent("beforehide", this) === false){
32585             return;
32586         }
32587         if(this.shadow){
32588             this.shadow.hide();
32589         }
32590         if(this.shim) {
32591           this.shim.hide();
32592         }
32593         // sometimes animateTarget seems to get set.. causing problems...
32594         // this just double checks..
32595         if(this.animateTarget && Roo.get(this.animateTarget)) {
32596            this.animHide(callback);
32597         }else{
32598             this.el.hide();
32599             this.hideEl(callback);
32600         }
32601         return this;
32602     },
32603
32604     // private
32605     hideEl : function(callback){
32606         this.proxy.hide();
32607         if(this.modal){
32608             this.mask.hide();
32609             Roo.get(document.body).removeClass("x-body-masked");
32610         }
32611         this.fireEvent("hide", this);
32612         if(typeof callback == "function"){
32613             callback();
32614         }
32615     },
32616
32617     // private
32618     hideAction : function(){
32619         this.setLeft("-10000px");
32620         this.setTop("-10000px");
32621         this.setStyle("visibility", "hidden");
32622     },
32623
32624     // private
32625     refreshSize : function(){
32626         this.size = this.el.getSize();
32627         this.xy = this.el.getXY();
32628         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32629     },
32630
32631     // private
32632     // z-index is managed by the DialogManager and may be overwritten at any time
32633     setZIndex : function(index){
32634         if(this.modal){
32635             this.mask.setStyle("z-index", index);
32636         }
32637         if(this.shim){
32638             this.shim.setStyle("z-index", ++index);
32639         }
32640         if(this.shadow){
32641             this.shadow.setZIndex(++index);
32642         }
32643         this.el.setStyle("z-index", ++index);
32644         if(this.proxy){
32645             this.proxy.setStyle("z-index", ++index);
32646         }
32647         if(this.resizer){
32648             this.resizer.proxy.setStyle("z-index", ++index);
32649         }
32650
32651         this.lastZIndex = index;
32652     },
32653
32654     /**
32655      * Returns the element for this dialog
32656      * @return {Roo.Element} The underlying dialog Element
32657      */
32658     getEl : function(){
32659         return this.el;
32660     }
32661 });
32662
32663 /**
32664  * @class Roo.DialogManager
32665  * Provides global access to BasicDialogs that have been created and
32666  * support for z-indexing (layering) multiple open dialogs.
32667  */
32668 Roo.DialogManager = function(){
32669     var list = {};
32670     var accessList = [];
32671     var front = null;
32672
32673     // private
32674     var sortDialogs = function(d1, d2){
32675         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32676     };
32677
32678     // private
32679     var orderDialogs = function(){
32680         accessList.sort(sortDialogs);
32681         var seed = Roo.DialogManager.zseed;
32682         for(var i = 0, len = accessList.length; i < len; i++){
32683             var dlg = accessList[i];
32684             if(dlg){
32685                 dlg.setZIndex(seed + (i*10));
32686             }
32687         }
32688     };
32689
32690     return {
32691         /**
32692          * The starting z-index for BasicDialogs (defaults to 9000)
32693          * @type Number The z-index value
32694          */
32695         zseed : 9000,
32696
32697         // private
32698         register : function(dlg){
32699             list[dlg.id] = dlg;
32700             accessList.push(dlg);
32701         },
32702
32703         // private
32704         unregister : function(dlg){
32705             delete list[dlg.id];
32706             var i=0;
32707             var len=0;
32708             if(!accessList.indexOf){
32709                 for(  i = 0, len = accessList.length; i < len; i++){
32710                     if(accessList[i] == dlg){
32711                         accessList.splice(i, 1);
32712                         return;
32713                     }
32714                 }
32715             }else{
32716                  i = accessList.indexOf(dlg);
32717                 if(i != -1){
32718                     accessList.splice(i, 1);
32719                 }
32720             }
32721         },
32722
32723         /**
32724          * Gets a registered dialog by id
32725          * @param {String/Object} id The id of the dialog or a dialog
32726          * @return {Roo.BasicDialog} this
32727          */
32728         get : function(id){
32729             return typeof id == "object" ? id : list[id];
32730         },
32731
32732         /**
32733          * Brings the specified dialog to the front
32734          * @param {String/Object} dlg The id of the dialog or a dialog
32735          * @return {Roo.BasicDialog} this
32736          */
32737         bringToFront : function(dlg){
32738             dlg = this.get(dlg);
32739             if(dlg != front){
32740                 front = dlg;
32741                 dlg._lastAccess = new Date().getTime();
32742                 orderDialogs();
32743             }
32744             return dlg;
32745         },
32746
32747         /**
32748          * Sends the specified dialog to the back
32749          * @param {String/Object} dlg The id of the dialog or a dialog
32750          * @return {Roo.BasicDialog} this
32751          */
32752         sendToBack : function(dlg){
32753             dlg = this.get(dlg);
32754             dlg._lastAccess = -(new Date().getTime());
32755             orderDialogs();
32756             return dlg;
32757         },
32758
32759         /**
32760          * Hides all dialogs
32761          */
32762         hideAll : function(){
32763             for(var id in list){
32764                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32765                     list[id].hide();
32766                 }
32767             }
32768         }
32769     };
32770 }();
32771
32772 /**
32773  * @class Roo.LayoutDialog
32774  * @extends Roo.BasicDialog
32775  * Dialog which provides adjustments for working with a layout in a Dialog.
32776  * Add your necessary layout config options to the dialog's config.<br>
32777  * Example usage (including a nested layout):
32778  * <pre><code>
32779 if(!dialog){
32780     dialog = new Roo.LayoutDialog("download-dlg", {
32781         modal: true,
32782         width:600,
32783         height:450,
32784         shadow:true,
32785         minWidth:500,
32786         minHeight:350,
32787         autoTabs:true,
32788         proxyDrag:true,
32789         // layout config merges with the dialog config
32790         center:{
32791             tabPosition: "top",
32792             alwaysShowTabs: true
32793         }
32794     });
32795     dialog.addKeyListener(27, dialog.hide, dialog);
32796     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32797     dialog.addButton("Build It!", this.getDownload, this);
32798
32799     // we can even add nested layouts
32800     var innerLayout = new Roo.BorderLayout("dl-inner", {
32801         east: {
32802             initialSize: 200,
32803             autoScroll:true,
32804             split:true
32805         },
32806         center: {
32807             autoScroll:true
32808         }
32809     });
32810     innerLayout.beginUpdate();
32811     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32812     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32813     innerLayout.endUpdate(true);
32814
32815     var layout = dialog.getLayout();
32816     layout.beginUpdate();
32817     layout.add("center", new Roo.ContentPanel("standard-panel",
32818                         {title: "Download the Source", fitToFrame:true}));
32819     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32820                {title: "Build your own roo.js"}));
32821     layout.getRegion("center").showPanel(sp);
32822     layout.endUpdate();
32823 }
32824 </code></pre>
32825     * @constructor
32826     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32827     * @param {Object} config configuration options
32828   */
32829 Roo.LayoutDialog = function(el, cfg){
32830     
32831     var config=  cfg;
32832     if (typeof(cfg) == 'undefined') {
32833         config = Roo.apply({}, el);
32834         // not sure why we use documentElement here.. - it should always be body.
32835         // IE7 borks horribly if we use documentElement.
32836         // webkit also does not like documentElement - it creates a body element...
32837         el = Roo.get( document.body || document.documentElement ).createChild();
32838         //config.autoCreate = true;
32839     }
32840     
32841     
32842     config.autoTabs = false;
32843     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32844     this.body.setStyle({overflow:"hidden", position:"relative"});
32845     this.layout = new Roo.BorderLayout(this.body.dom, config);
32846     this.layout.monitorWindowResize = false;
32847     this.el.addClass("x-dlg-auto-layout");
32848     // fix case when center region overwrites center function
32849     this.center = Roo.BasicDialog.prototype.center;
32850     this.on("show", this.layout.layout, this.layout, true);
32851     if (config.items) {
32852         var xitems = config.items;
32853         delete config.items;
32854         Roo.each(xitems, this.addxtype, this);
32855     }
32856     
32857     
32858 };
32859 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32860     /**
32861      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32862      * @deprecated
32863      */
32864     endUpdate : function(){
32865         this.layout.endUpdate();
32866     },
32867
32868     /**
32869      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32870      *  @deprecated
32871      */
32872     beginUpdate : function(){
32873         this.layout.beginUpdate();
32874     },
32875
32876     /**
32877      * Get the BorderLayout for this dialog
32878      * @return {Roo.BorderLayout}
32879      */
32880     getLayout : function(){
32881         return this.layout;
32882     },
32883
32884     showEl : function(){
32885         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32886         if(Roo.isIE7){
32887             this.layout.layout();
32888         }
32889     },
32890
32891     // private
32892     // Use the syncHeightBeforeShow config option to control this automatically
32893     syncBodyHeight : function(){
32894         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32895         if(this.layout){this.layout.layout();}
32896     },
32897     
32898       /**
32899      * Add an xtype element (actually adds to the layout.)
32900      * @return {Object} xdata xtype object data.
32901      */
32902     
32903     addxtype : function(c) {
32904         return this.layout.addxtype(c);
32905     }
32906 });/*
32907  * Based on:
32908  * Ext JS Library 1.1.1
32909  * Copyright(c) 2006-2007, Ext JS, LLC.
32910  *
32911  * Originally Released Under LGPL - original licence link has changed is not relivant.
32912  *
32913  * Fork - LGPL
32914  * <script type="text/javascript">
32915  */
32916  
32917 /**
32918  * @class Roo.MessageBox
32919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32920  * Example usage:
32921  *<pre><code>
32922 // Basic alert:
32923 Roo.Msg.alert('Status', 'Changes saved successfully.');
32924
32925 // Prompt for user data:
32926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32927     if (btn == 'ok'){
32928         // process text value...
32929     }
32930 });
32931
32932 // Show a dialog using config options:
32933 Roo.Msg.show({
32934    title:'Save Changes?',
32935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32936    buttons: Roo.Msg.YESNOCANCEL,
32937    fn: processResult,
32938    animEl: 'elId'
32939 });
32940 </code></pre>
32941  * @singleton
32942  */
32943 Roo.MessageBox = function(){
32944     var dlg, opt, mask, waitTimer;
32945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32946     var buttons, activeTextEl, bwidth;
32947
32948     // private
32949     var handleButton = function(button){
32950         dlg.hide();
32951         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32952     };
32953
32954     // private
32955     var handleHide = function(){
32956         if(opt && opt.cls){
32957             dlg.el.removeClass(opt.cls);
32958         }
32959         if(waitTimer){
32960             Roo.TaskMgr.stop(waitTimer);
32961             waitTimer = null;
32962         }
32963     };
32964
32965     // private
32966     var updateButtons = function(b){
32967         var width = 0;
32968         if(!b){
32969             buttons["ok"].hide();
32970             buttons["cancel"].hide();
32971             buttons["yes"].hide();
32972             buttons["no"].hide();
32973             dlg.footer.dom.style.display = 'none';
32974             return width;
32975         }
32976         dlg.footer.dom.style.display = '';
32977         for(var k in buttons){
32978             if(typeof buttons[k] != "function"){
32979                 if(b[k]){
32980                     buttons[k].show();
32981                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
32982                     width += buttons[k].el.getWidth()+15;
32983                 }else{
32984                     buttons[k].hide();
32985                 }
32986             }
32987         }
32988         return width;
32989     };
32990
32991     // private
32992     var handleEsc = function(d, k, e){
32993         if(opt && opt.closable !== false){
32994             dlg.hide();
32995         }
32996         if(e){
32997             e.stopEvent();
32998         }
32999     };
33000
33001     return {
33002         /**
33003          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33004          * @return {Roo.BasicDialog} The BasicDialog element
33005          */
33006         getDialog : function(){
33007            if(!dlg){
33008                 dlg = new Roo.BasicDialog("x-msg-box", {
33009                     autoCreate : true,
33010                     shadow: true,
33011                     draggable: true,
33012                     resizable:false,
33013                     constraintoviewport:false,
33014                     fixedcenter:true,
33015                     collapsible : false,
33016                     shim:true,
33017                     modal: true,
33018                     width:400, height:100,
33019                     buttonAlign:"center",
33020                     closeClick : function(){
33021                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33022                             handleButton("no");
33023                         }else{
33024                             handleButton("cancel");
33025                         }
33026                     }
33027                 });
33028                 dlg.on("hide", handleHide);
33029                 mask = dlg.mask;
33030                 dlg.addKeyListener(27, handleEsc);
33031                 buttons = {};
33032                 var bt = this.buttonText;
33033                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33034                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33035                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33036                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33037                 bodyEl = dlg.body.createChild({
33038
33039                     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>'
33040                 });
33041                 msgEl = bodyEl.dom.firstChild;
33042                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33043                 textboxEl.enableDisplayMode();
33044                 textboxEl.addKeyListener([10,13], function(){
33045                     if(dlg.isVisible() && opt && opt.buttons){
33046                         if(opt.buttons.ok){
33047                             handleButton("ok");
33048                         }else if(opt.buttons.yes){
33049                             handleButton("yes");
33050                         }
33051                     }
33052                 });
33053                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33054                 textareaEl.enableDisplayMode();
33055                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33056                 progressEl.enableDisplayMode();
33057                 var pf = progressEl.dom.firstChild;
33058                 if (pf) {
33059                     pp = Roo.get(pf.firstChild);
33060                     pp.setHeight(pf.offsetHeight);
33061                 }
33062                 
33063             }
33064             return dlg;
33065         },
33066
33067         /**
33068          * Updates the message box body text
33069          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33070          * the XHTML-compliant non-breaking space character '&amp;#160;')
33071          * @return {Roo.MessageBox} This message box
33072          */
33073         updateText : function(text){
33074             if(!dlg.isVisible() && !opt.width){
33075                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33076             }
33077             msgEl.innerHTML = text || '&#160;';
33078       
33079             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33080             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33081             var w = Math.max(
33082                     Math.min(opt.width || cw , this.maxWidth), 
33083                     Math.max(opt.minWidth || this.minWidth, bwidth)
33084             );
33085             if(opt.prompt){
33086                 activeTextEl.setWidth(w);
33087             }
33088             if(dlg.isVisible()){
33089                 dlg.fixedcenter = false;
33090             }
33091             // to big, make it scroll. = But as usual stupid IE does not support
33092             // !important..
33093             
33094             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33095                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33096                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33097             } else {
33098                 bodyEl.dom.style.height = '';
33099                 bodyEl.dom.style.overflowY = '';
33100             }
33101             if (cw > w) {
33102                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33103             } else {
33104                 bodyEl.dom.style.overflowX = '';
33105             }
33106             
33107             dlg.setContentSize(w, bodyEl.getHeight());
33108             if(dlg.isVisible()){
33109                 dlg.fixedcenter = true;
33110             }
33111             return this;
33112         },
33113
33114         /**
33115          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33116          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33117          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33118          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33119          * @return {Roo.MessageBox} This message box
33120          */
33121         updateProgress : function(value, text){
33122             if(text){
33123                 this.updateText(text);
33124             }
33125             if (pp) { // weird bug on my firefox - for some reason this is not defined
33126                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33127             }
33128             return this;
33129         },        
33130
33131         /**
33132          * Returns true if the message box is currently displayed
33133          * @return {Boolean} True if the message box is visible, else false
33134          */
33135         isVisible : function(){
33136             return dlg && dlg.isVisible();  
33137         },
33138
33139         /**
33140          * Hides the message box if it is displayed
33141          */
33142         hide : function(){
33143             if(this.isVisible()){
33144                 dlg.hide();
33145             }  
33146         },
33147
33148         /**
33149          * Displays a new message box, or reinitializes an existing message box, based on the config options
33150          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33151          * The following config object properties are supported:
33152          * <pre>
33153 Property    Type             Description
33154 ----------  ---------------  ------------------------------------------------------------------------------------
33155 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33156                                    closes (defaults to undefined)
33157 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33158                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33159 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33160                                    progress and wait dialogs will ignore this property and always hide the
33161                                    close button as they can only be closed programmatically.
33162 cls               String           A custom CSS class to apply to the message box element
33163 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33164                                    displayed (defaults to 75)
33165 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33166                                    function will be btn (the name of the button that was clicked, if applicable,
33167                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33168                                    Progress and wait dialogs will ignore this option since they do not respond to
33169                                    user actions and can only be closed programmatically, so any required function
33170                                    should be called by the same code after it closes the dialog.
33171 icon              String           A CSS class that provides a background image to be used as an icon for
33172                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33173 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33174 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33175 modal             Boolean          False to allow user interaction with the page while the message box is
33176                                    displayed (defaults to true)
33177 msg               String           A string that will replace the existing message box body text (defaults
33178                                    to the XHTML-compliant non-breaking space character '&#160;')
33179 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33180 progress          Boolean          True to display a progress bar (defaults to false)
33181 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33182 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33183 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33184 title             String           The title text
33185 value             String           The string value to set into the active textbox element if displayed
33186 wait              Boolean          True to display a progress bar (defaults to false)
33187 width             Number           The width of the dialog in pixels
33188 </pre>
33189          *
33190          * Example usage:
33191          * <pre><code>
33192 Roo.Msg.show({
33193    title: 'Address',
33194    msg: 'Please enter your address:',
33195    width: 300,
33196    buttons: Roo.MessageBox.OKCANCEL,
33197    multiline: true,
33198    fn: saveAddress,
33199    animEl: 'addAddressBtn'
33200 });
33201 </code></pre>
33202          * @param {Object} config Configuration options
33203          * @return {Roo.MessageBox} This message box
33204          */
33205         show : function(options)
33206         {
33207             
33208             // this causes nightmares if you show one dialog after another
33209             // especially on callbacks..
33210              
33211             if(this.isVisible()){
33212                 
33213                 this.hide();
33214                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33215                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33216                 Roo.log("New Dialog Message:" +  options.msg )
33217                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33218                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33219                 
33220             }
33221             var d = this.getDialog();
33222             opt = options;
33223             d.setTitle(opt.title || "&#160;");
33224             d.close.setDisplayed(opt.closable !== false);
33225             activeTextEl = textboxEl;
33226             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33227             if(opt.prompt){
33228                 if(opt.multiline){
33229                     textboxEl.hide();
33230                     textareaEl.show();
33231                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33232                         opt.multiline : this.defaultTextHeight);
33233                     activeTextEl = textareaEl;
33234                 }else{
33235                     textboxEl.show();
33236                     textareaEl.hide();
33237                 }
33238             }else{
33239                 textboxEl.hide();
33240                 textareaEl.hide();
33241             }
33242             progressEl.setDisplayed(opt.progress === true);
33243             this.updateProgress(0);
33244             activeTextEl.dom.value = opt.value || "";
33245             if(opt.prompt){
33246                 dlg.setDefaultButton(activeTextEl);
33247             }else{
33248                 var bs = opt.buttons;
33249                 var db = null;
33250                 if(bs && bs.ok){
33251                     db = buttons["ok"];
33252                 }else if(bs && bs.yes){
33253                     db = buttons["yes"];
33254                 }
33255                 dlg.setDefaultButton(db);
33256             }
33257             bwidth = updateButtons(opt.buttons);
33258             this.updateText(opt.msg);
33259             if(opt.cls){
33260                 d.el.addClass(opt.cls);
33261             }
33262             d.proxyDrag = opt.proxyDrag === true;
33263             d.modal = opt.modal !== false;
33264             d.mask = opt.modal !== false ? mask : false;
33265             if(!d.isVisible()){
33266                 // force it to the end of the z-index stack so it gets a cursor in FF
33267                 document.body.appendChild(dlg.el.dom);
33268                 d.animateTarget = null;
33269                 d.show(options.animEl);
33270             }
33271             return this;
33272         },
33273
33274         /**
33275          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33276          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33277          * and closing the message box when the process is complete.
33278          * @param {String} title The title bar text
33279          * @param {String} msg The message box body text
33280          * @return {Roo.MessageBox} This message box
33281          */
33282         progress : function(title, msg){
33283             this.show({
33284                 title : title,
33285                 msg : msg,
33286                 buttons: false,
33287                 progress:true,
33288                 closable:false,
33289                 minWidth: this.minProgressWidth,
33290                 modal : true
33291             });
33292             return this;
33293         },
33294
33295         /**
33296          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33297          * If a callback function is passed it will be called after the user clicks the button, and the
33298          * id of the button that was clicked will be passed as the only parameter to the callback
33299          * (could also be the top-right close button).
33300          * @param {String} title The title bar text
33301          * @param {String} msg The message box body text
33302          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33303          * @param {Object} scope (optional) The scope of the callback function
33304          * @return {Roo.MessageBox} This message box
33305          */
33306         alert : function(title, msg, fn, scope){
33307             this.show({
33308                 title : title,
33309                 msg : msg,
33310                 buttons: this.OK,
33311                 fn: fn,
33312                 scope : scope,
33313                 modal : true
33314             });
33315             return this;
33316         },
33317
33318         /**
33319          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33320          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33321          * You are responsible for closing the message box when the process is complete.
33322          * @param {String} msg The message box body text
33323          * @param {String} title (optional) The title bar text
33324          * @return {Roo.MessageBox} This message box
33325          */
33326         wait : function(msg, title){
33327             this.show({
33328                 title : title,
33329                 msg : msg,
33330                 buttons: false,
33331                 closable:false,
33332                 progress:true,
33333                 modal:true,
33334                 width:300,
33335                 wait:true
33336             });
33337             waitTimer = Roo.TaskMgr.start({
33338                 run: function(i){
33339                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33340                 },
33341                 interval: 1000
33342             });
33343             return this;
33344         },
33345
33346         /**
33347          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33348          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33349          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33350          * @param {String} title The title bar text
33351          * @param {String} msg The message box body text
33352          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33353          * @param {Object} scope (optional) The scope of the callback function
33354          * @return {Roo.MessageBox} This message box
33355          */
33356         confirm : function(title, msg, fn, scope){
33357             this.show({
33358                 title : title,
33359                 msg : msg,
33360                 buttons: this.YESNO,
33361                 fn: fn,
33362                 scope : scope,
33363                 modal : true
33364             });
33365             return this;
33366         },
33367
33368         /**
33369          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33370          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33371          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33372          * (could also be the top-right close button) and the text that was entered will be passed as the two
33373          * parameters to the callback.
33374          * @param {String} title The title bar text
33375          * @param {String} msg The message box body text
33376          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33377          * @param {Object} scope (optional) The scope of the callback function
33378          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33379          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33380          * @return {Roo.MessageBox} This message box
33381          */
33382         prompt : function(title, msg, fn, scope, multiline){
33383             this.show({
33384                 title : title,
33385                 msg : msg,
33386                 buttons: this.OKCANCEL,
33387                 fn: fn,
33388                 minWidth:250,
33389                 scope : scope,
33390                 prompt:true,
33391                 multiline: multiline,
33392                 modal : true
33393             });
33394             return this;
33395         },
33396
33397         /**
33398          * Button config that displays a single OK button
33399          * @type Object
33400          */
33401         OK : {ok:true},
33402         /**
33403          * Button config that displays Yes and No buttons
33404          * @type Object
33405          */
33406         YESNO : {yes:true, no:true},
33407         /**
33408          * Button config that displays OK and Cancel buttons
33409          * @type Object
33410          */
33411         OKCANCEL : {ok:true, cancel:true},
33412         /**
33413          * Button config that displays Yes, No and Cancel buttons
33414          * @type Object
33415          */
33416         YESNOCANCEL : {yes:true, no:true, cancel:true},
33417
33418         /**
33419          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33420          * @type Number
33421          */
33422         defaultTextHeight : 75,
33423         /**
33424          * The maximum width in pixels of the message box (defaults to 600)
33425          * @type Number
33426          */
33427         maxWidth : 600,
33428         /**
33429          * The minimum width in pixels of the message box (defaults to 100)
33430          * @type Number
33431          */
33432         minWidth : 100,
33433         /**
33434          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33435          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33436          * @type Number
33437          */
33438         minProgressWidth : 250,
33439         /**
33440          * An object containing the default button text strings that can be overriden for localized language support.
33441          * Supported properties are: ok, cancel, yes and no.
33442          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33443          * @type Object
33444          */
33445         buttonText : {
33446             ok : "OK",
33447             cancel : "Cancel",
33448             yes : "Yes",
33449             no : "No"
33450         }
33451     };
33452 }();
33453
33454 /**
33455  * Shorthand for {@link Roo.MessageBox}
33456  */
33457 Roo.Msg = Roo.MessageBox;/*
33458  * Based on:
33459  * Ext JS Library 1.1.1
33460  * Copyright(c) 2006-2007, Ext JS, LLC.
33461  *
33462  * Originally Released Under LGPL - original licence link has changed is not relivant.
33463  *
33464  * Fork - LGPL
33465  * <script type="text/javascript">
33466  */
33467 /**
33468  * @class Roo.QuickTips
33469  * Provides attractive and customizable tooltips for any element.
33470  * @singleton
33471  */
33472 Roo.QuickTips = function(){
33473     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33474     var ce, bd, xy, dd;
33475     var visible = false, disabled = true, inited = false;
33476     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33477     
33478     var onOver = function(e){
33479         if(disabled){
33480             return;
33481         }
33482         var t = e.getTarget();
33483         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33484             return;
33485         }
33486         if(ce && t == ce.el){
33487             clearTimeout(hideProc);
33488             return;
33489         }
33490         if(t && tagEls[t.id]){
33491             tagEls[t.id].el = t;
33492             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33493             return;
33494         }
33495         var ttp, et = Roo.fly(t);
33496         var ns = cfg.namespace;
33497         if(tm.interceptTitles && t.title){
33498             ttp = t.title;
33499             t.qtip = ttp;
33500             t.removeAttribute("title");
33501             e.preventDefault();
33502         }else{
33503             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33504         }
33505         if(ttp){
33506             showProc = show.defer(tm.showDelay, tm, [{
33507                 el: t, 
33508                 text: ttp, 
33509                 width: et.getAttributeNS(ns, cfg.width),
33510                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33511                 title: et.getAttributeNS(ns, cfg.title),
33512                     cls: et.getAttributeNS(ns, cfg.cls)
33513             }]);
33514         }
33515     };
33516     
33517     var onOut = function(e){
33518         clearTimeout(showProc);
33519         var t = e.getTarget();
33520         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33521             hideProc = setTimeout(hide, tm.hideDelay);
33522         }
33523     };
33524     
33525     var onMove = function(e){
33526         if(disabled){
33527             return;
33528         }
33529         xy = e.getXY();
33530         xy[1] += 18;
33531         if(tm.trackMouse && ce){
33532             el.setXY(xy);
33533         }
33534     };
33535     
33536     var onDown = function(e){
33537         clearTimeout(showProc);
33538         clearTimeout(hideProc);
33539         if(!e.within(el)){
33540             if(tm.hideOnClick){
33541                 hide();
33542                 tm.disable();
33543                 tm.enable.defer(100, tm);
33544             }
33545         }
33546     };
33547     
33548     var getPad = function(){
33549         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33550     };
33551
33552     var show = function(o){
33553         if(disabled){
33554             return;
33555         }
33556         clearTimeout(dismissProc);
33557         ce = o;
33558         if(removeCls){ // in case manually hidden
33559             el.removeClass(removeCls);
33560             removeCls = null;
33561         }
33562         if(ce.cls){
33563             el.addClass(ce.cls);
33564             removeCls = ce.cls;
33565         }
33566         if(ce.title){
33567             tipTitle.update(ce.title);
33568             tipTitle.show();
33569         }else{
33570             tipTitle.update('');
33571             tipTitle.hide();
33572         }
33573         el.dom.style.width  = tm.maxWidth+'px';
33574         //tipBody.dom.style.width = '';
33575         tipBodyText.update(o.text);
33576         var p = getPad(), w = ce.width;
33577         if(!w){
33578             var td = tipBodyText.dom;
33579             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33580             if(aw > tm.maxWidth){
33581                 w = tm.maxWidth;
33582             }else if(aw < tm.minWidth){
33583                 w = tm.minWidth;
33584             }else{
33585                 w = aw;
33586             }
33587         }
33588         //tipBody.setWidth(w);
33589         el.setWidth(parseInt(w, 10) + p);
33590         if(ce.autoHide === false){
33591             close.setDisplayed(true);
33592             if(dd){
33593                 dd.unlock();
33594             }
33595         }else{
33596             close.setDisplayed(false);
33597             if(dd){
33598                 dd.lock();
33599             }
33600         }
33601         if(xy){
33602             el.avoidY = xy[1]-18;
33603             el.setXY(xy);
33604         }
33605         if(tm.animate){
33606             el.setOpacity(.1);
33607             el.setStyle("visibility", "visible");
33608             el.fadeIn({callback: afterShow});
33609         }else{
33610             afterShow();
33611         }
33612     };
33613     
33614     var afterShow = function(){
33615         if(ce){
33616             el.show();
33617             esc.enable();
33618             if(tm.autoDismiss && ce.autoHide !== false){
33619                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33620             }
33621         }
33622     };
33623     
33624     var hide = function(noanim){
33625         clearTimeout(dismissProc);
33626         clearTimeout(hideProc);
33627         ce = null;
33628         if(el.isVisible()){
33629             esc.disable();
33630             if(noanim !== true && tm.animate){
33631                 el.fadeOut({callback: afterHide});
33632             }else{
33633                 afterHide();
33634             } 
33635         }
33636     };
33637     
33638     var afterHide = function(){
33639         el.hide();
33640         if(removeCls){
33641             el.removeClass(removeCls);
33642             removeCls = null;
33643         }
33644     };
33645     
33646     return {
33647         /**
33648         * @cfg {Number} minWidth
33649         * The minimum width of the quick tip (defaults to 40)
33650         */
33651        minWidth : 40,
33652         /**
33653         * @cfg {Number} maxWidth
33654         * The maximum width of the quick tip (defaults to 300)
33655         */
33656        maxWidth : 300,
33657         /**
33658         * @cfg {Boolean} interceptTitles
33659         * True to automatically use the element's DOM title value if available (defaults to false)
33660         */
33661        interceptTitles : false,
33662         /**
33663         * @cfg {Boolean} trackMouse
33664         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33665         */
33666        trackMouse : false,
33667         /**
33668         * @cfg {Boolean} hideOnClick
33669         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33670         */
33671        hideOnClick : true,
33672         /**
33673         * @cfg {Number} showDelay
33674         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33675         */
33676        showDelay : 500,
33677         /**
33678         * @cfg {Number} hideDelay
33679         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33680         */
33681        hideDelay : 200,
33682         /**
33683         * @cfg {Boolean} autoHide
33684         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33685         * Used in conjunction with hideDelay.
33686         */
33687        autoHide : true,
33688         /**
33689         * @cfg {Boolean}
33690         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33691         * (defaults to true).  Used in conjunction with autoDismissDelay.
33692         */
33693        autoDismiss : true,
33694         /**
33695         * @cfg {Number}
33696         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33697         */
33698        autoDismissDelay : 5000,
33699        /**
33700         * @cfg {Boolean} animate
33701         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33702         */
33703        animate : false,
33704
33705        /**
33706         * @cfg {String} title
33707         * Title text to display (defaults to '').  This can be any valid HTML markup.
33708         */
33709         title: '',
33710        /**
33711         * @cfg {String} text
33712         * Body text to display (defaults to '').  This can be any valid HTML markup.
33713         */
33714         text : '',
33715        /**
33716         * @cfg {String} cls
33717         * A CSS class to apply to the base quick tip element (defaults to '').
33718         */
33719         cls : '',
33720        /**
33721         * @cfg {Number} width
33722         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33723         * minWidth or maxWidth.
33724         */
33725         width : null,
33726
33727     /**
33728      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33729      * or display QuickTips in a page.
33730      */
33731        init : function(){
33732           tm = Roo.QuickTips;
33733           cfg = tm.tagConfig;
33734           if(!inited){
33735               if(!Roo.isReady){ // allow calling of init() before onReady
33736                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33737                   return;
33738               }
33739               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33740               el.fxDefaults = {stopFx: true};
33741               // maximum custom styling
33742               //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>');
33743               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>');              
33744               tipTitle = el.child('h3');
33745               tipTitle.enableDisplayMode("block");
33746               tipBody = el.child('div.x-tip-bd');
33747               tipBodyText = el.child('div.x-tip-bd-inner');
33748               //bdLeft = el.child('div.x-tip-bd-left');
33749               //bdRight = el.child('div.x-tip-bd-right');
33750               close = el.child('div.x-tip-close');
33751               close.enableDisplayMode("block");
33752               close.on("click", hide);
33753               var d = Roo.get(document);
33754               d.on("mousedown", onDown);
33755               d.on("mouseover", onOver);
33756               d.on("mouseout", onOut);
33757               d.on("mousemove", onMove);
33758               esc = d.addKeyListener(27, hide);
33759               esc.disable();
33760               if(Roo.dd.DD){
33761                   dd = el.initDD("default", null, {
33762                       onDrag : function(){
33763                           el.sync();  
33764                       }
33765                   });
33766                   dd.setHandleElId(tipTitle.id);
33767                   dd.lock();
33768               }
33769               inited = true;
33770           }
33771           this.enable(); 
33772        },
33773
33774     /**
33775      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33776      * are supported:
33777      * <pre>
33778 Property    Type                   Description
33779 ----------  ---------------------  ------------------------------------------------------------------------
33780 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33781      * </ul>
33782      * @param {Object} config The config object
33783      */
33784        register : function(config){
33785            var cs = config instanceof Array ? config : arguments;
33786            for(var i = 0, len = cs.length; i < len; i++) {
33787                var c = cs[i];
33788                var target = c.target;
33789                if(target){
33790                    if(target instanceof Array){
33791                        for(var j = 0, jlen = target.length; j < jlen; j++){
33792                            tagEls[target[j]] = c;
33793                        }
33794                    }else{
33795                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33796                    }
33797                }
33798            }
33799        },
33800
33801     /**
33802      * Removes this quick tip from its element and destroys it.
33803      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33804      */
33805        unregister : function(el){
33806            delete tagEls[Roo.id(el)];
33807        },
33808
33809     /**
33810      * Enable this quick tip.
33811      */
33812        enable : function(){
33813            if(inited && disabled){
33814                locks.pop();
33815                if(locks.length < 1){
33816                    disabled = false;
33817                }
33818            }
33819        },
33820
33821     /**
33822      * Disable this quick tip.
33823      */
33824        disable : function(){
33825           disabled = true;
33826           clearTimeout(showProc);
33827           clearTimeout(hideProc);
33828           clearTimeout(dismissProc);
33829           if(ce){
33830               hide(true);
33831           }
33832           locks.push(1);
33833        },
33834
33835     /**
33836      * Returns true if the quick tip is enabled, else false.
33837      */
33838        isEnabled : function(){
33839             return !disabled;
33840        },
33841
33842         // private
33843        tagConfig : {
33844            namespace : "roo", // was ext?? this may break..
33845            alt_namespace : "ext",
33846            attribute : "qtip",
33847            width : "width",
33848            target : "target",
33849            title : "qtitle",
33850            hide : "hide",
33851            cls : "qclass"
33852        }
33853    };
33854 }();
33855
33856 // backwards compat
33857 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33858  * Based on:
33859  * Ext JS Library 1.1.1
33860  * Copyright(c) 2006-2007, Ext JS, LLC.
33861  *
33862  * Originally Released Under LGPL - original licence link has changed is not relivant.
33863  *
33864  * Fork - LGPL
33865  * <script type="text/javascript">
33866  */
33867  
33868
33869 /**
33870  * @class Roo.tree.TreePanel
33871  * @extends Roo.data.Tree
33872
33873  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33874  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33875  * @cfg {Boolean} enableDD true to enable drag and drop
33876  * @cfg {Boolean} enableDrag true to enable just drag
33877  * @cfg {Boolean} enableDrop true to enable just drop
33878  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33879  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33880  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33881  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33882  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33883  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33884  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33885  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33886  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33887  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33888  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33889  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33890  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33891  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33892  * @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>
33893  * @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>
33894  * 
33895  * @constructor
33896  * @param {String/HTMLElement/Element} el The container element
33897  * @param {Object} config
33898  */
33899 Roo.tree.TreePanel = function(el, config){
33900     var root = false;
33901     var loader = false;
33902     if (config.root) {
33903         root = config.root;
33904         delete config.root;
33905     }
33906     if (config.loader) {
33907         loader = config.loader;
33908         delete config.loader;
33909     }
33910     
33911     Roo.apply(this, config);
33912     Roo.tree.TreePanel.superclass.constructor.call(this);
33913     this.el = Roo.get(el);
33914     this.el.addClass('x-tree');
33915     //console.log(root);
33916     if (root) {
33917         this.setRootNode( Roo.factory(root, Roo.tree));
33918     }
33919     if (loader) {
33920         this.loader = Roo.factory(loader, Roo.tree);
33921     }
33922    /**
33923     * Read-only. The id of the container element becomes this TreePanel's id.
33924     */
33925     this.id = this.el.id;
33926     this.addEvents({
33927         /**
33928         * @event beforeload
33929         * Fires before a node is loaded, return false to cancel
33930         * @param {Node} node The node being loaded
33931         */
33932         "beforeload" : true,
33933         /**
33934         * @event load
33935         * Fires when a node is loaded
33936         * @param {Node} node The node that was loaded
33937         */
33938         "load" : true,
33939         /**
33940         * @event textchange
33941         * Fires when the text for a node is changed
33942         * @param {Node} node The node
33943         * @param {String} text The new text
33944         * @param {String} oldText The old text
33945         */
33946         "textchange" : true,
33947         /**
33948         * @event beforeexpand
33949         * Fires before a node is expanded, return false to cancel.
33950         * @param {Node} node The node
33951         * @param {Boolean} deep
33952         * @param {Boolean} anim
33953         */
33954         "beforeexpand" : true,
33955         /**
33956         * @event beforecollapse
33957         * Fires before a node is collapsed, return false to cancel.
33958         * @param {Node} node The node
33959         * @param {Boolean} deep
33960         * @param {Boolean} anim
33961         */
33962         "beforecollapse" : true,
33963         /**
33964         * @event expand
33965         * Fires when a node is expanded
33966         * @param {Node} node The node
33967         */
33968         "expand" : true,
33969         /**
33970         * @event disabledchange
33971         * Fires when the disabled status of a node changes
33972         * @param {Node} node The node
33973         * @param {Boolean} disabled
33974         */
33975         "disabledchange" : true,
33976         /**
33977         * @event collapse
33978         * Fires when a node is collapsed
33979         * @param {Node} node The node
33980         */
33981         "collapse" : true,
33982         /**
33983         * @event beforeclick
33984         * Fires before click processing on a node. Return false to cancel the default action.
33985         * @param {Node} node The node
33986         * @param {Roo.EventObject} e The event object
33987         */
33988         "beforeclick":true,
33989         /**
33990         * @event checkchange
33991         * Fires when a node with a checkbox's checked property changes
33992         * @param {Node} this This node
33993         * @param {Boolean} checked
33994         */
33995         "checkchange":true,
33996         /**
33997         * @event click
33998         * Fires when a node is clicked
33999         * @param {Node} node The node
34000         * @param {Roo.EventObject} e The event object
34001         */
34002         "click":true,
34003         /**
34004         * @event dblclick
34005         * Fires when a node is double clicked
34006         * @param {Node} node The node
34007         * @param {Roo.EventObject} e The event object
34008         */
34009         "dblclick":true,
34010         /**
34011         * @event contextmenu
34012         * Fires when a node is right clicked
34013         * @param {Node} node The node
34014         * @param {Roo.EventObject} e The event object
34015         */
34016         "contextmenu":true,
34017         /**
34018         * @event beforechildrenrendered
34019         * Fires right before the child nodes for a node are rendered
34020         * @param {Node} node The node
34021         */
34022         "beforechildrenrendered":true,
34023         /**
34024         * @event startdrag
34025         * Fires when a node starts being dragged
34026         * @param {Roo.tree.TreePanel} this
34027         * @param {Roo.tree.TreeNode} node
34028         * @param {event} e The raw browser event
34029         */ 
34030        "startdrag" : true,
34031        /**
34032         * @event enddrag
34033         * Fires when a drag operation is complete
34034         * @param {Roo.tree.TreePanel} this
34035         * @param {Roo.tree.TreeNode} node
34036         * @param {event} e The raw browser event
34037         */
34038        "enddrag" : true,
34039        /**
34040         * @event dragdrop
34041         * Fires when a dragged node is dropped on a valid DD target
34042         * @param {Roo.tree.TreePanel} this
34043         * @param {Roo.tree.TreeNode} node
34044         * @param {DD} dd The dd it was dropped on
34045         * @param {event} e The raw browser event
34046         */
34047        "dragdrop" : true,
34048        /**
34049         * @event beforenodedrop
34050         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34051         * passed to handlers has the following properties:<br />
34052         * <ul style="padding:5px;padding-left:16px;">
34053         * <li>tree - The TreePanel</li>
34054         * <li>target - The node being targeted for the drop</li>
34055         * <li>data - The drag data from the drag source</li>
34056         * <li>point - The point of the drop - append, above or below</li>
34057         * <li>source - The drag source</li>
34058         * <li>rawEvent - Raw mouse event</li>
34059         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34060         * to be inserted by setting them on this object.</li>
34061         * <li>cancel - Set this to true to cancel the drop.</li>
34062         * </ul>
34063         * @param {Object} dropEvent
34064         */
34065        "beforenodedrop" : true,
34066        /**
34067         * @event nodedrop
34068         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34069         * passed to handlers has the following properties:<br />
34070         * <ul style="padding:5px;padding-left:16px;">
34071         * <li>tree - The TreePanel</li>
34072         * <li>target - The node being targeted for the drop</li>
34073         * <li>data - The drag data from the drag source</li>
34074         * <li>point - The point of the drop - append, above or below</li>
34075         * <li>source - The drag source</li>
34076         * <li>rawEvent - Raw mouse event</li>
34077         * <li>dropNode - Dropped node(s).</li>
34078         * </ul>
34079         * @param {Object} dropEvent
34080         */
34081        "nodedrop" : true,
34082         /**
34083         * @event nodedragover
34084         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34085         * passed to handlers has the following properties:<br />
34086         * <ul style="padding:5px;padding-left:16px;">
34087         * <li>tree - The TreePanel</li>
34088         * <li>target - The node being targeted for the drop</li>
34089         * <li>data - The drag data from the drag source</li>
34090         * <li>point - The point of the drop - append, above or below</li>
34091         * <li>source - The drag source</li>
34092         * <li>rawEvent - Raw mouse event</li>
34093         * <li>dropNode - Drop node(s) provided by the source.</li>
34094         * <li>cancel - Set this to true to signal drop not allowed.</li>
34095         * </ul>
34096         * @param {Object} dragOverEvent
34097         */
34098        "nodedragover" : true
34099         
34100     });
34101     if(this.singleExpand){
34102        this.on("beforeexpand", this.restrictExpand, this);
34103     }
34104     if (this.editor) {
34105         this.editor.tree = this;
34106         this.editor = Roo.factory(this.editor, Roo.tree);
34107     }
34108     
34109     if (this.selModel) {
34110         this.selModel = Roo.factory(this.selModel, Roo.tree);
34111     }
34112    
34113 };
34114 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34115     rootVisible : true,
34116     animate: Roo.enableFx,
34117     lines : true,
34118     enableDD : false,
34119     hlDrop : Roo.enableFx,
34120   
34121     renderer: false,
34122     
34123     rendererTip: false,
34124     // private
34125     restrictExpand : function(node){
34126         var p = node.parentNode;
34127         if(p){
34128             if(p.expandedChild && p.expandedChild.parentNode == p){
34129                 p.expandedChild.collapse();
34130             }
34131             p.expandedChild = node;
34132         }
34133     },
34134
34135     // private override
34136     setRootNode : function(node){
34137         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34138         if(!this.rootVisible){
34139             node.ui = new Roo.tree.RootTreeNodeUI(node);
34140         }
34141         return node;
34142     },
34143
34144     /**
34145      * Returns the container element for this TreePanel
34146      */
34147     getEl : function(){
34148         return this.el;
34149     },
34150
34151     /**
34152      * Returns the default TreeLoader for this TreePanel
34153      */
34154     getLoader : function(){
34155         return this.loader;
34156     },
34157
34158     /**
34159      * Expand all nodes
34160      */
34161     expandAll : function(){
34162         this.root.expand(true);
34163     },
34164
34165     /**
34166      * Collapse all nodes
34167      */
34168     collapseAll : function(){
34169         this.root.collapse(true);
34170     },
34171
34172     /**
34173      * Returns the selection model used by this TreePanel
34174      */
34175     getSelectionModel : function(){
34176         if(!this.selModel){
34177             this.selModel = new Roo.tree.DefaultSelectionModel();
34178         }
34179         return this.selModel;
34180     },
34181
34182     /**
34183      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34184      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34185      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34186      * @return {Array}
34187      */
34188     getChecked : function(a, startNode){
34189         startNode = startNode || this.root;
34190         var r = [];
34191         var f = function(){
34192             if(this.attributes.checked){
34193                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34194             }
34195         }
34196         startNode.cascade(f);
34197         return r;
34198     },
34199
34200     /**
34201      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34202      * @param {String} path
34203      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34204      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34205      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34206      */
34207     expandPath : function(path, attr, callback){
34208         attr = attr || "id";
34209         var keys = path.split(this.pathSeparator);
34210         var curNode = this.root;
34211         if(curNode.attributes[attr] != keys[1]){ // invalid root
34212             if(callback){
34213                 callback(false, null);
34214             }
34215             return;
34216         }
34217         var index = 1;
34218         var f = function(){
34219             if(++index == keys.length){
34220                 if(callback){
34221                     callback(true, curNode);
34222                 }
34223                 return;
34224             }
34225             var c = curNode.findChild(attr, keys[index]);
34226             if(!c){
34227                 if(callback){
34228                     callback(false, curNode);
34229                 }
34230                 return;
34231             }
34232             curNode = c;
34233             c.expand(false, false, f);
34234         };
34235         curNode.expand(false, false, f);
34236     },
34237
34238     /**
34239      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34240      * @param {String} path
34241      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34242      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34243      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34244      */
34245     selectPath : function(path, attr, callback){
34246         attr = attr || "id";
34247         var keys = path.split(this.pathSeparator);
34248         var v = keys.pop();
34249         if(keys.length > 0){
34250             var f = function(success, node){
34251                 if(success && node){
34252                     var n = node.findChild(attr, v);
34253                     if(n){
34254                         n.select();
34255                         if(callback){
34256                             callback(true, n);
34257                         }
34258                     }else if(callback){
34259                         callback(false, n);
34260                     }
34261                 }else{
34262                     if(callback){
34263                         callback(false, n);
34264                     }
34265                 }
34266             };
34267             this.expandPath(keys.join(this.pathSeparator), attr, f);
34268         }else{
34269             this.root.select();
34270             if(callback){
34271                 callback(true, this.root);
34272             }
34273         }
34274     },
34275
34276     getTreeEl : function(){
34277         return this.el;
34278     },
34279
34280     /**
34281      * Trigger rendering of this TreePanel
34282      */
34283     render : function(){
34284         if (this.innerCt) {
34285             return this; // stop it rendering more than once!!
34286         }
34287         
34288         this.innerCt = this.el.createChild({tag:"ul",
34289                cls:"x-tree-root-ct " +
34290                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34291
34292         if(this.containerScroll){
34293             Roo.dd.ScrollManager.register(this.el);
34294         }
34295         if((this.enableDD || this.enableDrop) && !this.dropZone){
34296            /**
34297             * The dropZone used by this tree if drop is enabled
34298             * @type Roo.tree.TreeDropZone
34299             */
34300              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34301                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34302            });
34303         }
34304         if((this.enableDD || this.enableDrag) && !this.dragZone){
34305            /**
34306             * The dragZone used by this tree if drag is enabled
34307             * @type Roo.tree.TreeDragZone
34308             */
34309             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34310                ddGroup: this.ddGroup || "TreeDD",
34311                scroll: this.ddScroll
34312            });
34313         }
34314         this.getSelectionModel().init(this);
34315         if (!this.root) {
34316             Roo.log("ROOT not set in tree");
34317             return this;
34318         }
34319         this.root.render();
34320         if(!this.rootVisible){
34321             this.root.renderChildren();
34322         }
34323         return this;
34324     }
34325 });/*
34326  * Based on:
34327  * Ext JS Library 1.1.1
34328  * Copyright(c) 2006-2007, Ext JS, LLC.
34329  *
34330  * Originally Released Under LGPL - original licence link has changed is not relivant.
34331  *
34332  * Fork - LGPL
34333  * <script type="text/javascript">
34334  */
34335  
34336
34337 /**
34338  * @class Roo.tree.DefaultSelectionModel
34339  * @extends Roo.util.Observable
34340  * The default single selection for a TreePanel.
34341  * @param {Object} cfg Configuration
34342  */
34343 Roo.tree.DefaultSelectionModel = function(cfg){
34344    this.selNode = null;
34345    
34346    
34347    
34348    this.addEvents({
34349        /**
34350         * @event selectionchange
34351         * Fires when the selected node changes
34352         * @param {DefaultSelectionModel} this
34353         * @param {TreeNode} node the new selection
34354         */
34355        "selectionchange" : true,
34356
34357        /**
34358         * @event beforeselect
34359         * Fires before the selected node changes, return false to cancel the change
34360         * @param {DefaultSelectionModel} this
34361         * @param {TreeNode} node the new selection
34362         * @param {TreeNode} node the old selection
34363         */
34364        "beforeselect" : true
34365    });
34366    
34367     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34368 };
34369
34370 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34371     init : function(tree){
34372         this.tree = tree;
34373         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34374         tree.on("click", this.onNodeClick, this);
34375     },
34376     
34377     onNodeClick : function(node, e){
34378         if (e.ctrlKey && this.selNode == node)  {
34379             this.unselect(node);
34380             return;
34381         }
34382         this.select(node);
34383     },
34384     
34385     /**
34386      * Select a node.
34387      * @param {TreeNode} node The node to select
34388      * @return {TreeNode} The selected node
34389      */
34390     select : function(node){
34391         var last = this.selNode;
34392         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34393             if(last){
34394                 last.ui.onSelectedChange(false);
34395             }
34396             this.selNode = node;
34397             node.ui.onSelectedChange(true);
34398             this.fireEvent("selectionchange", this, node, last);
34399         }
34400         return node;
34401     },
34402     
34403     /**
34404      * Deselect a node.
34405      * @param {TreeNode} node The node to unselect
34406      */
34407     unselect : function(node){
34408         if(this.selNode == node){
34409             this.clearSelections();
34410         }    
34411     },
34412     
34413     /**
34414      * Clear all selections
34415      */
34416     clearSelections : function(){
34417         var n = this.selNode;
34418         if(n){
34419             n.ui.onSelectedChange(false);
34420             this.selNode = null;
34421             this.fireEvent("selectionchange", this, null);
34422         }
34423         return n;
34424     },
34425     
34426     /**
34427      * Get the selected node
34428      * @return {TreeNode} The selected node
34429      */
34430     getSelectedNode : function(){
34431         return this.selNode;    
34432     },
34433     
34434     /**
34435      * Returns true if the node is selected
34436      * @param {TreeNode} node The node to check
34437      * @return {Boolean}
34438      */
34439     isSelected : function(node){
34440         return this.selNode == node;  
34441     },
34442
34443     /**
34444      * Selects the node above the selected node in the tree, intelligently walking the nodes
34445      * @return TreeNode The new selection
34446      */
34447     selectPrevious : function(){
34448         var s = this.selNode || this.lastSelNode;
34449         if(!s){
34450             return null;
34451         }
34452         var ps = s.previousSibling;
34453         if(ps){
34454             if(!ps.isExpanded() || ps.childNodes.length < 1){
34455                 return this.select(ps);
34456             } else{
34457                 var lc = ps.lastChild;
34458                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34459                     lc = lc.lastChild;
34460                 }
34461                 return this.select(lc);
34462             }
34463         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34464             return this.select(s.parentNode);
34465         }
34466         return null;
34467     },
34468
34469     /**
34470      * Selects the node above the selected node in the tree, intelligently walking the nodes
34471      * @return TreeNode The new selection
34472      */
34473     selectNext : function(){
34474         var s = this.selNode || this.lastSelNode;
34475         if(!s){
34476             return null;
34477         }
34478         if(s.firstChild && s.isExpanded()){
34479              return this.select(s.firstChild);
34480          }else if(s.nextSibling){
34481              return this.select(s.nextSibling);
34482          }else if(s.parentNode){
34483             var newS = null;
34484             s.parentNode.bubble(function(){
34485                 if(this.nextSibling){
34486                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34487                     return false;
34488                 }
34489             });
34490             return newS;
34491          }
34492         return null;
34493     },
34494
34495     onKeyDown : function(e){
34496         var s = this.selNode || this.lastSelNode;
34497         // undesirable, but required
34498         var sm = this;
34499         if(!s){
34500             return;
34501         }
34502         var k = e.getKey();
34503         switch(k){
34504              case e.DOWN:
34505                  e.stopEvent();
34506                  this.selectNext();
34507              break;
34508              case e.UP:
34509                  e.stopEvent();
34510                  this.selectPrevious();
34511              break;
34512              case e.RIGHT:
34513                  e.preventDefault();
34514                  if(s.hasChildNodes()){
34515                      if(!s.isExpanded()){
34516                          s.expand();
34517                      }else if(s.firstChild){
34518                          this.select(s.firstChild, e);
34519                      }
34520                  }
34521              break;
34522              case e.LEFT:
34523                  e.preventDefault();
34524                  if(s.hasChildNodes() && s.isExpanded()){
34525                      s.collapse();
34526                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34527                      this.select(s.parentNode, e);
34528                  }
34529              break;
34530         };
34531     }
34532 });
34533
34534 /**
34535  * @class Roo.tree.MultiSelectionModel
34536  * @extends Roo.util.Observable
34537  * Multi selection for a TreePanel.
34538  * @param {Object} cfg Configuration
34539  */
34540 Roo.tree.MultiSelectionModel = function(){
34541    this.selNodes = [];
34542    this.selMap = {};
34543    this.addEvents({
34544        /**
34545         * @event selectionchange
34546         * Fires when the selected nodes change
34547         * @param {MultiSelectionModel} this
34548         * @param {Array} nodes Array of the selected nodes
34549         */
34550        "selectionchange" : true
34551    });
34552    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34553    
34554 };
34555
34556 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34557     init : function(tree){
34558         this.tree = tree;
34559         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34560         tree.on("click", this.onNodeClick, this);
34561     },
34562     
34563     onNodeClick : function(node, e){
34564         this.select(node, e, e.ctrlKey);
34565     },
34566     
34567     /**
34568      * Select a node.
34569      * @param {TreeNode} node The node to select
34570      * @param {EventObject} e (optional) An event associated with the selection
34571      * @param {Boolean} keepExisting True to retain existing selections
34572      * @return {TreeNode} The selected node
34573      */
34574     select : function(node, e, keepExisting){
34575         if(keepExisting !== true){
34576             this.clearSelections(true);
34577         }
34578         if(this.isSelected(node)){
34579             this.lastSelNode = node;
34580             return node;
34581         }
34582         this.selNodes.push(node);
34583         this.selMap[node.id] = node;
34584         this.lastSelNode = node;
34585         node.ui.onSelectedChange(true);
34586         this.fireEvent("selectionchange", this, this.selNodes);
34587         return node;
34588     },
34589     
34590     /**
34591      * Deselect a node.
34592      * @param {TreeNode} node The node to unselect
34593      */
34594     unselect : function(node){
34595         if(this.selMap[node.id]){
34596             node.ui.onSelectedChange(false);
34597             var sn = this.selNodes;
34598             var index = -1;
34599             if(sn.indexOf){
34600                 index = sn.indexOf(node);
34601             }else{
34602                 for(var i = 0, len = sn.length; i < len; i++){
34603                     if(sn[i] == node){
34604                         index = i;
34605                         break;
34606                     }
34607                 }
34608             }
34609             if(index != -1){
34610                 this.selNodes.splice(index, 1);
34611             }
34612             delete this.selMap[node.id];
34613             this.fireEvent("selectionchange", this, this.selNodes);
34614         }
34615     },
34616     
34617     /**
34618      * Clear all selections
34619      */
34620     clearSelections : function(suppressEvent){
34621         var sn = this.selNodes;
34622         if(sn.length > 0){
34623             for(var i = 0, len = sn.length; i < len; i++){
34624                 sn[i].ui.onSelectedChange(false);
34625             }
34626             this.selNodes = [];
34627             this.selMap = {};
34628             if(suppressEvent !== true){
34629                 this.fireEvent("selectionchange", this, this.selNodes);
34630             }
34631         }
34632     },
34633     
34634     /**
34635      * Returns true if the node is selected
34636      * @param {TreeNode} node The node to check
34637      * @return {Boolean}
34638      */
34639     isSelected : function(node){
34640         return this.selMap[node.id] ? true : false;  
34641     },
34642     
34643     /**
34644      * Returns an array of the selected nodes
34645      * @return {Array}
34646      */
34647     getSelectedNodes : function(){
34648         return this.selNodes;    
34649     },
34650
34651     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34652
34653     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34654
34655     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34656 });/*
34657  * Based on:
34658  * Ext JS Library 1.1.1
34659  * Copyright(c) 2006-2007, Ext JS, LLC.
34660  *
34661  * Originally Released Under LGPL - original licence link has changed is not relivant.
34662  *
34663  * Fork - LGPL
34664  * <script type="text/javascript">
34665  */
34666  
34667 /**
34668  * @class Roo.tree.TreeNode
34669  * @extends Roo.data.Node
34670  * @cfg {String} text The text for this node
34671  * @cfg {Boolean} expanded true to start the node expanded
34672  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34673  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34674  * @cfg {Boolean} disabled true to start the node disabled
34675  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34676  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34677  * @cfg {String} cls A css class to be added to the node
34678  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34679  * @cfg {String} href URL of the link used for the node (defaults to #)
34680  * @cfg {String} hrefTarget target frame for the link
34681  * @cfg {String} qtip An Ext QuickTip for the node
34682  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34683  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34684  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34685  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34686  * (defaults to undefined with no checkbox rendered)
34687  * @constructor
34688  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34689  */
34690 Roo.tree.TreeNode = function(attributes){
34691     attributes = attributes || {};
34692     if(typeof attributes == "string"){
34693         attributes = {text: attributes};
34694     }
34695     this.childrenRendered = false;
34696     this.rendered = false;
34697     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34698     this.expanded = attributes.expanded === true;
34699     this.isTarget = attributes.isTarget !== false;
34700     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34701     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34702
34703     /**
34704      * Read-only. The text for this node. To change it use setText().
34705      * @type String
34706      */
34707     this.text = attributes.text;
34708     /**
34709      * True if this node is disabled.
34710      * @type Boolean
34711      */
34712     this.disabled = attributes.disabled === true;
34713
34714     this.addEvents({
34715         /**
34716         * @event textchange
34717         * Fires when the text for this node is changed
34718         * @param {Node} this This node
34719         * @param {String} text The new text
34720         * @param {String} oldText The old text
34721         */
34722         "textchange" : true,
34723         /**
34724         * @event beforeexpand
34725         * Fires before this node is expanded, return false to cancel.
34726         * @param {Node} this This node
34727         * @param {Boolean} deep
34728         * @param {Boolean} anim
34729         */
34730         "beforeexpand" : true,
34731         /**
34732         * @event beforecollapse
34733         * Fires before this node is collapsed, return false to cancel.
34734         * @param {Node} this This node
34735         * @param {Boolean} deep
34736         * @param {Boolean} anim
34737         */
34738         "beforecollapse" : true,
34739         /**
34740         * @event expand
34741         * Fires when this node is expanded
34742         * @param {Node} this This node
34743         */
34744         "expand" : true,
34745         /**
34746         * @event disabledchange
34747         * Fires when the disabled status of this node changes
34748         * @param {Node} this This node
34749         * @param {Boolean} disabled
34750         */
34751         "disabledchange" : true,
34752         /**
34753         * @event collapse
34754         * Fires when this node is collapsed
34755         * @param {Node} this This node
34756         */
34757         "collapse" : true,
34758         /**
34759         * @event beforeclick
34760         * Fires before click processing. Return false to cancel the default action.
34761         * @param {Node} this This node
34762         * @param {Roo.EventObject} e The event object
34763         */
34764         "beforeclick":true,
34765         /**
34766         * @event checkchange
34767         * Fires when a node with a checkbox's checked property changes
34768         * @param {Node} this This node
34769         * @param {Boolean} checked
34770         */
34771         "checkchange":true,
34772         /**
34773         * @event click
34774         * Fires when this node is clicked
34775         * @param {Node} this This node
34776         * @param {Roo.EventObject} e The event object
34777         */
34778         "click":true,
34779         /**
34780         * @event dblclick
34781         * Fires when this node is double clicked
34782         * @param {Node} this This node
34783         * @param {Roo.EventObject} e The event object
34784         */
34785         "dblclick":true,
34786         /**
34787         * @event contextmenu
34788         * Fires when this node is right clicked
34789         * @param {Node} this This node
34790         * @param {Roo.EventObject} e The event object
34791         */
34792         "contextmenu":true,
34793         /**
34794         * @event beforechildrenrendered
34795         * Fires right before the child nodes for this node are rendered
34796         * @param {Node} this This node
34797         */
34798         "beforechildrenrendered":true
34799     });
34800
34801     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34802
34803     /**
34804      * Read-only. The UI for this node
34805      * @type TreeNodeUI
34806      */
34807     this.ui = new uiClass(this);
34808     
34809     // finally support items[]
34810     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34811         return;
34812     }
34813     
34814     
34815     Roo.each(this.attributes.items, function(c) {
34816         this.appendChild(Roo.factory(c,Roo.Tree));
34817     }, this);
34818     delete this.attributes.items;
34819     
34820     
34821     
34822 };
34823 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34824     preventHScroll: true,
34825     /**
34826      * Returns true if this node is expanded
34827      * @return {Boolean}
34828      */
34829     isExpanded : function(){
34830         return this.expanded;
34831     },
34832
34833     /**
34834      * Returns the UI object for this node
34835      * @return {TreeNodeUI}
34836      */
34837     getUI : function(){
34838         return this.ui;
34839     },
34840
34841     // private override
34842     setFirstChild : function(node){
34843         var of = this.firstChild;
34844         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34845         if(this.childrenRendered && of && node != of){
34846             of.renderIndent(true, true);
34847         }
34848         if(this.rendered){
34849             this.renderIndent(true, true);
34850         }
34851     },
34852
34853     // private override
34854     setLastChild : function(node){
34855         var ol = this.lastChild;
34856         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34857         if(this.childrenRendered && ol && node != ol){
34858             ol.renderIndent(true, true);
34859         }
34860         if(this.rendered){
34861             this.renderIndent(true, true);
34862         }
34863     },
34864
34865     // these methods are overridden to provide lazy rendering support
34866     // private override
34867     appendChild : function()
34868     {
34869         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34870         if(node && this.childrenRendered){
34871             node.render();
34872         }
34873         this.ui.updateExpandIcon();
34874         return node;
34875     },
34876
34877     // private override
34878     removeChild : function(node){
34879         this.ownerTree.getSelectionModel().unselect(node);
34880         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34881         // if it's been rendered remove dom node
34882         if(this.childrenRendered){
34883             node.ui.remove();
34884         }
34885         if(this.childNodes.length < 1){
34886             this.collapse(false, false);
34887         }else{
34888             this.ui.updateExpandIcon();
34889         }
34890         if(!this.firstChild) {
34891             this.childrenRendered = false;
34892         }
34893         return node;
34894     },
34895
34896     // private override
34897     insertBefore : function(node, refNode){
34898         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34899         if(newNode && refNode && this.childrenRendered){
34900             node.render();
34901         }
34902         this.ui.updateExpandIcon();
34903         return newNode;
34904     },
34905
34906     /**
34907      * Sets the text for this node
34908      * @param {String} text
34909      */
34910     setText : function(text){
34911         var oldText = this.text;
34912         this.text = text;
34913         this.attributes.text = text;
34914         if(this.rendered){ // event without subscribing
34915             this.ui.onTextChange(this, text, oldText);
34916         }
34917         this.fireEvent("textchange", this, text, oldText);
34918     },
34919
34920     /**
34921      * Triggers selection of this node
34922      */
34923     select : function(){
34924         this.getOwnerTree().getSelectionModel().select(this);
34925     },
34926
34927     /**
34928      * Triggers deselection of this node
34929      */
34930     unselect : function(){
34931         this.getOwnerTree().getSelectionModel().unselect(this);
34932     },
34933
34934     /**
34935      * Returns true if this node is selected
34936      * @return {Boolean}
34937      */
34938     isSelected : function(){
34939         return this.getOwnerTree().getSelectionModel().isSelected(this);
34940     },
34941
34942     /**
34943      * Expand this node.
34944      * @param {Boolean} deep (optional) True to expand all children as well
34945      * @param {Boolean} anim (optional) false to cancel the default animation
34946      * @param {Function} callback (optional) A callback to be called when
34947      * expanding this node completes (does not wait for deep expand to complete).
34948      * Called with 1 parameter, this node.
34949      */
34950     expand : function(deep, anim, callback){
34951         if(!this.expanded){
34952             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34953                 return;
34954             }
34955             if(!this.childrenRendered){
34956                 this.renderChildren();
34957             }
34958             this.expanded = true;
34959             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
34960                 this.ui.animExpand(function(){
34961                     this.fireEvent("expand", this);
34962                     if(typeof callback == "function"){
34963                         callback(this);
34964                     }
34965                     if(deep === true){
34966                         this.expandChildNodes(true);
34967                     }
34968                 }.createDelegate(this));
34969                 return;
34970             }else{
34971                 this.ui.expand();
34972                 this.fireEvent("expand", this);
34973                 if(typeof callback == "function"){
34974                     callback(this);
34975                 }
34976             }
34977         }else{
34978            if(typeof callback == "function"){
34979                callback(this);
34980            }
34981         }
34982         if(deep === true){
34983             this.expandChildNodes(true);
34984         }
34985     },
34986
34987     isHiddenRoot : function(){
34988         return this.isRoot && !this.getOwnerTree().rootVisible;
34989     },
34990
34991     /**
34992      * Collapse this node.
34993      * @param {Boolean} deep (optional) True to collapse all children as well
34994      * @param {Boolean} anim (optional) false to cancel the default animation
34995      */
34996     collapse : function(deep, anim){
34997         if(this.expanded && !this.isHiddenRoot()){
34998             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
34999                 return;
35000             }
35001             this.expanded = false;
35002             if((this.getOwnerTree().animate && anim !== false) || anim){
35003                 this.ui.animCollapse(function(){
35004                     this.fireEvent("collapse", this);
35005                     if(deep === true){
35006                         this.collapseChildNodes(true);
35007                     }
35008                 }.createDelegate(this));
35009                 return;
35010             }else{
35011                 this.ui.collapse();
35012                 this.fireEvent("collapse", this);
35013             }
35014         }
35015         if(deep === true){
35016             var cs = this.childNodes;
35017             for(var i = 0, len = cs.length; i < len; i++) {
35018                 cs[i].collapse(true, false);
35019             }
35020         }
35021     },
35022
35023     // private
35024     delayedExpand : function(delay){
35025         if(!this.expandProcId){
35026             this.expandProcId = this.expand.defer(delay, this);
35027         }
35028     },
35029
35030     // private
35031     cancelExpand : function(){
35032         if(this.expandProcId){
35033             clearTimeout(this.expandProcId);
35034         }
35035         this.expandProcId = false;
35036     },
35037
35038     /**
35039      * Toggles expanded/collapsed state of the node
35040      */
35041     toggle : function(){
35042         if(this.expanded){
35043             this.collapse();
35044         }else{
35045             this.expand();
35046         }
35047     },
35048
35049     /**
35050      * Ensures all parent nodes are expanded
35051      */
35052     ensureVisible : function(callback){
35053         var tree = this.getOwnerTree();
35054         tree.expandPath(this.parentNode.getPath(), false, function(){
35055             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35056             Roo.callback(callback);
35057         }.createDelegate(this));
35058     },
35059
35060     /**
35061      * Expand all child nodes
35062      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35063      */
35064     expandChildNodes : function(deep){
35065         var cs = this.childNodes;
35066         for(var i = 0, len = cs.length; i < len; i++) {
35067                 cs[i].expand(deep);
35068         }
35069     },
35070
35071     /**
35072      * Collapse all child nodes
35073      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35074      */
35075     collapseChildNodes : function(deep){
35076         var cs = this.childNodes;
35077         for(var i = 0, len = cs.length; i < len; i++) {
35078                 cs[i].collapse(deep);
35079         }
35080     },
35081
35082     /**
35083      * Disables this node
35084      */
35085     disable : function(){
35086         this.disabled = true;
35087         this.unselect();
35088         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35089             this.ui.onDisableChange(this, true);
35090         }
35091         this.fireEvent("disabledchange", this, true);
35092     },
35093
35094     /**
35095      * Enables this node
35096      */
35097     enable : function(){
35098         this.disabled = false;
35099         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35100             this.ui.onDisableChange(this, false);
35101         }
35102         this.fireEvent("disabledchange", this, false);
35103     },
35104
35105     // private
35106     renderChildren : function(suppressEvent){
35107         if(suppressEvent !== false){
35108             this.fireEvent("beforechildrenrendered", this);
35109         }
35110         var cs = this.childNodes;
35111         for(var i = 0, len = cs.length; i < len; i++){
35112             cs[i].render(true);
35113         }
35114         this.childrenRendered = true;
35115     },
35116
35117     // private
35118     sort : function(fn, scope){
35119         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35120         if(this.childrenRendered){
35121             var cs = this.childNodes;
35122             for(var i = 0, len = cs.length; i < len; i++){
35123                 cs[i].render(true);
35124             }
35125         }
35126     },
35127
35128     // private
35129     render : function(bulkRender){
35130         this.ui.render(bulkRender);
35131         if(!this.rendered){
35132             this.rendered = true;
35133             if(this.expanded){
35134                 this.expanded = false;
35135                 this.expand(false, false);
35136             }
35137         }
35138     },
35139
35140     // private
35141     renderIndent : function(deep, refresh){
35142         if(refresh){
35143             this.ui.childIndent = null;
35144         }
35145         this.ui.renderIndent();
35146         if(deep === true && this.childrenRendered){
35147             var cs = this.childNodes;
35148             for(var i = 0, len = cs.length; i < len; i++){
35149                 cs[i].renderIndent(true, refresh);
35150             }
35151         }
35152     }
35153 });/*
35154  * Based on:
35155  * Ext JS Library 1.1.1
35156  * Copyright(c) 2006-2007, Ext JS, LLC.
35157  *
35158  * Originally Released Under LGPL - original licence link has changed is not relivant.
35159  *
35160  * Fork - LGPL
35161  * <script type="text/javascript">
35162  */
35163  
35164 /**
35165  * @class Roo.tree.AsyncTreeNode
35166  * @extends Roo.tree.TreeNode
35167  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35168  * @constructor
35169  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35170  */
35171  Roo.tree.AsyncTreeNode = function(config){
35172     this.loaded = false;
35173     this.loading = false;
35174     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35175     /**
35176     * @event beforeload
35177     * Fires before this node is loaded, return false to cancel
35178     * @param {Node} this This node
35179     */
35180     this.addEvents({'beforeload':true, 'load': true});
35181     /**
35182     * @event load
35183     * Fires when this node is loaded
35184     * @param {Node} this This node
35185     */
35186     /**
35187      * The loader used by this node (defaults to using the tree's defined loader)
35188      * @type TreeLoader
35189      * @property loader
35190      */
35191 };
35192 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35193     expand : function(deep, anim, callback){
35194         if(this.loading){ // if an async load is already running, waiting til it's done
35195             var timer;
35196             var f = function(){
35197                 if(!this.loading){ // done loading
35198                     clearInterval(timer);
35199                     this.expand(deep, anim, callback);
35200                 }
35201             }.createDelegate(this);
35202             timer = setInterval(f, 200);
35203             return;
35204         }
35205         if(!this.loaded){
35206             if(this.fireEvent("beforeload", this) === false){
35207                 return;
35208             }
35209             this.loading = true;
35210             this.ui.beforeLoad(this);
35211             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35212             if(loader){
35213                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35214                 return;
35215             }
35216         }
35217         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35218     },
35219     
35220     /**
35221      * Returns true if this node is currently loading
35222      * @return {Boolean}
35223      */
35224     isLoading : function(){
35225         return this.loading;  
35226     },
35227     
35228     loadComplete : function(deep, anim, callback){
35229         this.loading = false;
35230         this.loaded = true;
35231         this.ui.afterLoad(this);
35232         this.fireEvent("load", this);
35233         this.expand(deep, anim, callback);
35234     },
35235     
35236     /**
35237      * Returns true if this node has been loaded
35238      * @return {Boolean}
35239      */
35240     isLoaded : function(){
35241         return this.loaded;
35242     },
35243     
35244     hasChildNodes : function(){
35245         if(!this.isLeaf() && !this.loaded){
35246             return true;
35247         }else{
35248             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35249         }
35250     },
35251
35252     /**
35253      * Trigger a reload for this node
35254      * @param {Function} callback
35255      */
35256     reload : function(callback){
35257         this.collapse(false, false);
35258         while(this.firstChild){
35259             this.removeChild(this.firstChild);
35260         }
35261         this.childrenRendered = false;
35262         this.loaded = false;
35263         if(this.isHiddenRoot()){
35264             this.expanded = false;
35265         }
35266         this.expand(false, false, callback);
35267     }
35268 });/*
35269  * Based on:
35270  * Ext JS Library 1.1.1
35271  * Copyright(c) 2006-2007, Ext JS, LLC.
35272  *
35273  * Originally Released Under LGPL - original licence link has changed is not relivant.
35274  *
35275  * Fork - LGPL
35276  * <script type="text/javascript">
35277  */
35278  
35279 /**
35280  * @class Roo.tree.TreeNodeUI
35281  * @constructor
35282  * @param {Object} node The node to render
35283  * The TreeNode UI implementation is separate from the
35284  * tree implementation. Unless you are customizing the tree UI,
35285  * you should never have to use this directly.
35286  */
35287 Roo.tree.TreeNodeUI = function(node){
35288     this.node = node;
35289     this.rendered = false;
35290     this.animating = false;
35291     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35292 };
35293
35294 Roo.tree.TreeNodeUI.prototype = {
35295     removeChild : function(node){
35296         if(this.rendered){
35297             this.ctNode.removeChild(node.ui.getEl());
35298         }
35299     },
35300
35301     beforeLoad : function(){
35302          this.addClass("x-tree-node-loading");
35303     },
35304
35305     afterLoad : function(){
35306          this.removeClass("x-tree-node-loading");
35307     },
35308
35309     onTextChange : function(node, text, oldText){
35310         if(this.rendered){
35311             this.textNode.innerHTML = text;
35312         }
35313     },
35314
35315     onDisableChange : function(node, state){
35316         this.disabled = state;
35317         if(state){
35318             this.addClass("x-tree-node-disabled");
35319         }else{
35320             this.removeClass("x-tree-node-disabled");
35321         }
35322     },
35323
35324     onSelectedChange : function(state){
35325         if(state){
35326             this.focus();
35327             this.addClass("x-tree-selected");
35328         }else{
35329             //this.blur();
35330             this.removeClass("x-tree-selected");
35331         }
35332     },
35333
35334     onMove : function(tree, node, oldParent, newParent, index, refNode){
35335         this.childIndent = null;
35336         if(this.rendered){
35337             var targetNode = newParent.ui.getContainer();
35338             if(!targetNode){//target not rendered
35339                 this.holder = document.createElement("div");
35340                 this.holder.appendChild(this.wrap);
35341                 return;
35342             }
35343             var insertBefore = refNode ? refNode.ui.getEl() : null;
35344             if(insertBefore){
35345                 targetNode.insertBefore(this.wrap, insertBefore);
35346             }else{
35347                 targetNode.appendChild(this.wrap);
35348             }
35349             this.node.renderIndent(true);
35350         }
35351     },
35352
35353     addClass : function(cls){
35354         if(this.elNode){
35355             Roo.fly(this.elNode).addClass(cls);
35356         }
35357     },
35358
35359     removeClass : function(cls){
35360         if(this.elNode){
35361             Roo.fly(this.elNode).removeClass(cls);
35362         }
35363     },
35364
35365     remove : function(){
35366         if(this.rendered){
35367             this.holder = document.createElement("div");
35368             this.holder.appendChild(this.wrap);
35369         }
35370     },
35371
35372     fireEvent : function(){
35373         return this.node.fireEvent.apply(this.node, arguments);
35374     },
35375
35376     initEvents : function(){
35377         this.node.on("move", this.onMove, this);
35378         var E = Roo.EventManager;
35379         var a = this.anchor;
35380
35381         var el = Roo.fly(a, '_treeui');
35382
35383         if(Roo.isOpera){ // opera render bug ignores the CSS
35384             el.setStyle("text-decoration", "none");
35385         }
35386
35387         el.on("click", this.onClick, this);
35388         el.on("dblclick", this.onDblClick, this);
35389
35390         if(this.checkbox){
35391             Roo.EventManager.on(this.checkbox,
35392                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35393         }
35394
35395         el.on("contextmenu", this.onContextMenu, this);
35396
35397         var icon = Roo.fly(this.iconNode);
35398         icon.on("click", this.onClick, this);
35399         icon.on("dblclick", this.onDblClick, this);
35400         icon.on("contextmenu", this.onContextMenu, this);
35401         E.on(this.ecNode, "click", this.ecClick, this, true);
35402
35403         if(this.node.disabled){
35404             this.addClass("x-tree-node-disabled");
35405         }
35406         if(this.node.hidden){
35407             this.addClass("x-tree-node-disabled");
35408         }
35409         var ot = this.node.getOwnerTree();
35410         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35411         if(dd && (!this.node.isRoot || ot.rootVisible)){
35412             Roo.dd.Registry.register(this.elNode, {
35413                 node: this.node,
35414                 handles: this.getDDHandles(),
35415                 isHandle: false
35416             });
35417         }
35418     },
35419
35420     getDDHandles : function(){
35421         return [this.iconNode, this.textNode];
35422     },
35423
35424     hide : function(){
35425         if(this.rendered){
35426             this.wrap.style.display = "none";
35427         }
35428     },
35429
35430     show : function(){
35431         if(this.rendered){
35432             this.wrap.style.display = "";
35433         }
35434     },
35435
35436     onContextMenu : function(e){
35437         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35438             e.preventDefault();
35439             this.focus();
35440             this.fireEvent("contextmenu", this.node, e);
35441         }
35442     },
35443
35444     onClick : function(e){
35445         if(this.dropping){
35446             e.stopEvent();
35447             return;
35448         }
35449         if(this.fireEvent("beforeclick", this.node, e) !== false){
35450             if(!this.disabled && this.node.attributes.href){
35451                 this.fireEvent("click", this.node, e);
35452                 return;
35453             }
35454             e.preventDefault();
35455             if(this.disabled){
35456                 return;
35457             }
35458
35459             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35460                 this.node.toggle();
35461             }
35462
35463             this.fireEvent("click", this.node, e);
35464         }else{
35465             e.stopEvent();
35466         }
35467     },
35468
35469     onDblClick : function(e){
35470         e.preventDefault();
35471         if(this.disabled){
35472             return;
35473         }
35474         if(this.checkbox){
35475             this.toggleCheck();
35476         }
35477         if(!this.animating && this.node.hasChildNodes()){
35478             this.node.toggle();
35479         }
35480         this.fireEvent("dblclick", this.node, e);
35481     },
35482
35483     onCheckChange : function(){
35484         var checked = this.checkbox.checked;
35485         this.node.attributes.checked = checked;
35486         this.fireEvent('checkchange', this.node, checked);
35487     },
35488
35489     ecClick : function(e){
35490         if(!this.animating && this.node.hasChildNodes()){
35491             this.node.toggle();
35492         }
35493     },
35494
35495     startDrop : function(){
35496         this.dropping = true;
35497     },
35498
35499     // delayed drop so the click event doesn't get fired on a drop
35500     endDrop : function(){
35501        setTimeout(function(){
35502            this.dropping = false;
35503        }.createDelegate(this), 50);
35504     },
35505
35506     expand : function(){
35507         this.updateExpandIcon();
35508         this.ctNode.style.display = "";
35509     },
35510
35511     focus : function(){
35512         if(!this.node.preventHScroll){
35513             try{this.anchor.focus();
35514             }catch(e){}
35515         }else if(!Roo.isIE){
35516             try{
35517                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35518                 var l = noscroll.scrollLeft;
35519                 this.anchor.focus();
35520                 noscroll.scrollLeft = l;
35521             }catch(e){}
35522         }
35523     },
35524
35525     toggleCheck : function(value){
35526         var cb = this.checkbox;
35527         if(cb){
35528             cb.checked = (value === undefined ? !cb.checked : value);
35529         }
35530     },
35531
35532     blur : function(){
35533         try{
35534             this.anchor.blur();
35535         }catch(e){}
35536     },
35537
35538     animExpand : function(callback){
35539         var ct = Roo.get(this.ctNode);
35540         ct.stopFx();
35541         if(!this.node.hasChildNodes()){
35542             this.updateExpandIcon();
35543             this.ctNode.style.display = "";
35544             Roo.callback(callback);
35545             return;
35546         }
35547         this.animating = true;
35548         this.updateExpandIcon();
35549
35550         ct.slideIn('t', {
35551            callback : function(){
35552                this.animating = false;
35553                Roo.callback(callback);
35554             },
35555             scope: this,
35556             duration: this.node.ownerTree.duration || .25
35557         });
35558     },
35559
35560     highlight : function(){
35561         var tree = this.node.getOwnerTree();
35562         Roo.fly(this.wrap).highlight(
35563             tree.hlColor || "C3DAF9",
35564             {endColor: tree.hlBaseColor}
35565         );
35566     },
35567
35568     collapse : function(){
35569         this.updateExpandIcon();
35570         this.ctNode.style.display = "none";
35571     },
35572
35573     animCollapse : function(callback){
35574         var ct = Roo.get(this.ctNode);
35575         ct.enableDisplayMode('block');
35576         ct.stopFx();
35577
35578         this.animating = true;
35579         this.updateExpandIcon();
35580
35581         ct.slideOut('t', {
35582             callback : function(){
35583                this.animating = false;
35584                Roo.callback(callback);
35585             },
35586             scope: this,
35587             duration: this.node.ownerTree.duration || .25
35588         });
35589     },
35590
35591     getContainer : function(){
35592         return this.ctNode;
35593     },
35594
35595     getEl : function(){
35596         return this.wrap;
35597     },
35598
35599     appendDDGhost : function(ghostNode){
35600         ghostNode.appendChild(this.elNode.cloneNode(true));
35601     },
35602
35603     getDDRepairXY : function(){
35604         return Roo.lib.Dom.getXY(this.iconNode);
35605     },
35606
35607     onRender : function(){
35608         this.render();
35609     },
35610
35611     render : function(bulkRender){
35612         var n = this.node, a = n.attributes;
35613         var targetNode = n.parentNode ?
35614               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35615
35616         if(!this.rendered){
35617             this.rendered = true;
35618
35619             this.renderElements(n, a, targetNode, bulkRender);
35620
35621             if(a.qtip){
35622                if(this.textNode.setAttributeNS){
35623                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35624                    if(a.qtipTitle){
35625                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35626                    }
35627                }else{
35628                    this.textNode.setAttribute("ext:qtip", a.qtip);
35629                    if(a.qtipTitle){
35630                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35631                    }
35632                }
35633             }else if(a.qtipCfg){
35634                 a.qtipCfg.target = Roo.id(this.textNode);
35635                 Roo.QuickTips.register(a.qtipCfg);
35636             }
35637             this.initEvents();
35638             if(!this.node.expanded){
35639                 this.updateExpandIcon();
35640             }
35641         }else{
35642             if(bulkRender === true) {
35643                 targetNode.appendChild(this.wrap);
35644             }
35645         }
35646     },
35647
35648     renderElements : function(n, a, targetNode, bulkRender)
35649     {
35650         // add some indent caching, this helps performance when rendering a large tree
35651         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35652         var t = n.getOwnerTree();
35653         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35654         if (typeof(n.attributes.html) != 'undefined') {
35655             txt = n.attributes.html;
35656         }
35657         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35658         var cb = typeof a.checked == 'boolean';
35659         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35660         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35661             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35662             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35663             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35664             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35665             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35666              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35667                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35668             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35669             "</li>"];
35670
35671         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35672             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35673                                 n.nextSibling.ui.getEl(), buf.join(""));
35674         }else{
35675             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35676         }
35677
35678         this.elNode = this.wrap.childNodes[0];
35679         this.ctNode = this.wrap.childNodes[1];
35680         var cs = this.elNode.childNodes;
35681         this.indentNode = cs[0];
35682         this.ecNode = cs[1];
35683         this.iconNode = cs[2];
35684         var index = 3;
35685         if(cb){
35686             this.checkbox = cs[3];
35687             index++;
35688         }
35689         this.anchor = cs[index];
35690         this.textNode = cs[index].firstChild;
35691     },
35692
35693     getAnchor : function(){
35694         return this.anchor;
35695     },
35696
35697     getTextEl : function(){
35698         return this.textNode;
35699     },
35700
35701     getIconEl : function(){
35702         return this.iconNode;
35703     },
35704
35705     isChecked : function(){
35706         return this.checkbox ? this.checkbox.checked : false;
35707     },
35708
35709     updateExpandIcon : function(){
35710         if(this.rendered){
35711             var n = this.node, c1, c2;
35712             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35713             var hasChild = n.hasChildNodes();
35714             if(hasChild){
35715                 if(n.expanded){
35716                     cls += "-minus";
35717                     c1 = "x-tree-node-collapsed";
35718                     c2 = "x-tree-node-expanded";
35719                 }else{
35720                     cls += "-plus";
35721                     c1 = "x-tree-node-expanded";
35722                     c2 = "x-tree-node-collapsed";
35723                 }
35724                 if(this.wasLeaf){
35725                     this.removeClass("x-tree-node-leaf");
35726                     this.wasLeaf = false;
35727                 }
35728                 if(this.c1 != c1 || this.c2 != c2){
35729                     Roo.fly(this.elNode).replaceClass(c1, c2);
35730                     this.c1 = c1; this.c2 = c2;
35731                 }
35732             }else{
35733                 // this changes non-leafs into leafs if they have no children.
35734                 // it's not very rational behaviour..
35735                 
35736                 if(!this.wasLeaf && this.node.leaf){
35737                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35738                     delete this.c1;
35739                     delete this.c2;
35740                     this.wasLeaf = true;
35741                 }
35742             }
35743             var ecc = "x-tree-ec-icon "+cls;
35744             if(this.ecc != ecc){
35745                 this.ecNode.className = ecc;
35746                 this.ecc = ecc;
35747             }
35748         }
35749     },
35750
35751     getChildIndent : function(){
35752         if(!this.childIndent){
35753             var buf = [];
35754             var p = this.node;
35755             while(p){
35756                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35757                     if(!p.isLast()) {
35758                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35759                     } else {
35760                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35761                     }
35762                 }
35763                 p = p.parentNode;
35764             }
35765             this.childIndent = buf.join("");
35766         }
35767         return this.childIndent;
35768     },
35769
35770     renderIndent : function(){
35771         if(this.rendered){
35772             var indent = "";
35773             var p = this.node.parentNode;
35774             if(p){
35775                 indent = p.ui.getChildIndent();
35776             }
35777             if(this.indentMarkup != indent){ // don't rerender if not required
35778                 this.indentNode.innerHTML = indent;
35779                 this.indentMarkup = indent;
35780             }
35781             this.updateExpandIcon();
35782         }
35783     }
35784 };
35785
35786 Roo.tree.RootTreeNodeUI = function(){
35787     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35788 };
35789 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35790     render : function(){
35791         if(!this.rendered){
35792             var targetNode = this.node.ownerTree.innerCt.dom;
35793             this.node.expanded = true;
35794             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35795             this.wrap = this.ctNode = targetNode.firstChild;
35796         }
35797     },
35798     collapse : function(){
35799     },
35800     expand : function(){
35801     }
35802 });/*
35803  * Based on:
35804  * Ext JS Library 1.1.1
35805  * Copyright(c) 2006-2007, Ext JS, LLC.
35806  *
35807  * Originally Released Under LGPL - original licence link has changed is not relivant.
35808  *
35809  * Fork - LGPL
35810  * <script type="text/javascript">
35811  */
35812 /**
35813  * @class Roo.tree.TreeLoader
35814  * @extends Roo.util.Observable
35815  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35816  * nodes from a specified URL. The response must be a javascript Array definition
35817  * who's elements are node definition objects. eg:
35818  * <pre><code>
35819 {  success : true,
35820    data :      [
35821    
35822     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35823     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35824     ]
35825 }
35826
35827
35828 </code></pre>
35829  * <br><br>
35830  * The old style respose with just an array is still supported, but not recommended.
35831  * <br><br>
35832  *
35833  * A server request is sent, and child nodes are loaded only when a node is expanded.
35834  * The loading node's id is passed to the server under the parameter name "node" to
35835  * enable the server to produce the correct child nodes.
35836  * <br><br>
35837  * To pass extra parameters, an event handler may be attached to the "beforeload"
35838  * event, and the parameters specified in the TreeLoader's baseParams property:
35839  * <pre><code>
35840     myTreeLoader.on("beforeload", function(treeLoader, node) {
35841         this.baseParams.category = node.attributes.category;
35842     }, this);
35843 </code></pre><
35844  * This would pass an HTTP parameter called "category" to the server containing
35845  * the value of the Node's "category" attribute.
35846  * @constructor
35847  * Creates a new Treeloader.
35848  * @param {Object} config A config object containing config properties.
35849  */
35850 Roo.tree.TreeLoader = function(config){
35851     this.baseParams = {};
35852     this.requestMethod = "POST";
35853     Roo.apply(this, config);
35854
35855     this.addEvents({
35856     
35857         /**
35858          * @event beforeload
35859          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35860          * @param {Object} This TreeLoader object.
35861          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35862          * @param {Object} callback The callback function specified in the {@link #load} call.
35863          */
35864         beforeload : true,
35865         /**
35866          * @event load
35867          * Fires when the node has been successfuly loaded.
35868          * @param {Object} This TreeLoader object.
35869          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35870          * @param {Object} response The response object containing the data from the server.
35871          */
35872         load : true,
35873         /**
35874          * @event loadexception
35875          * Fires if the network request failed.
35876          * @param {Object} This TreeLoader object.
35877          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35878          * @param {Object} response The response object containing the data from the server.
35879          */
35880         loadexception : true,
35881         /**
35882          * @event create
35883          * Fires before a node is created, enabling you to return custom Node types 
35884          * @param {Object} This TreeLoader object.
35885          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35886          */
35887         create : true
35888     });
35889
35890     Roo.tree.TreeLoader.superclass.constructor.call(this);
35891 };
35892
35893 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35894     /**
35895     * @cfg {String} dataUrl The URL from which to request a Json string which
35896     * specifies an array of node definition object representing the child nodes
35897     * to be loaded.
35898     */
35899     /**
35900     * @cfg {String} requestMethod either GET or POST
35901     * defaults to POST (due to BC)
35902     * to be loaded.
35903     */
35904     /**
35905     * @cfg {Object} baseParams (optional) An object containing properties which
35906     * specify HTTP parameters to be passed to each request for child nodes.
35907     */
35908     /**
35909     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35910     * created by this loader. If the attributes sent by the server have an attribute in this object,
35911     * they take priority.
35912     */
35913     /**
35914     * @cfg {Object} uiProviders (optional) An object containing properties which
35915     * 
35916     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35917     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35918     * <i>uiProvider</i> attribute of a returned child node is a string rather
35919     * than a reference to a TreeNodeUI implementation, this that string value
35920     * is used as a property name in the uiProviders object. You can define the provider named
35921     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35922     */
35923     uiProviders : {},
35924
35925     /**
35926     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35927     * child nodes before loading.
35928     */
35929     clearOnLoad : true,
35930
35931     /**
35932     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35933     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35934     * Grid query { data : [ .....] }
35935     */
35936     
35937     root : false,
35938      /**
35939     * @cfg {String} queryParam (optional) 
35940     * Name of the query as it will be passed on the querystring (defaults to 'node')
35941     * eg. the request will be ?node=[id]
35942     */
35943     
35944     
35945     queryParam: false,
35946     
35947     /**
35948      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35949      * This is called automatically when a node is expanded, but may be used to reload
35950      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35951      * @param {Roo.tree.TreeNode} node
35952      * @param {Function} callback
35953      */
35954     load : function(node, callback){
35955         if(this.clearOnLoad){
35956             while(node.firstChild){
35957                 node.removeChild(node.firstChild);
35958             }
35959         }
35960         if(node.attributes.children){ // preloaded json children
35961             var cs = node.attributes.children;
35962             for(var i = 0, len = cs.length; i < len; i++){
35963                 node.appendChild(this.createNode(cs[i]));
35964             }
35965             if(typeof callback == "function"){
35966                 callback();
35967             }
35968         }else if(this.dataUrl){
35969             this.requestData(node, callback);
35970         }
35971     },
35972
35973     getParams: function(node){
35974         var buf = [], bp = this.baseParams;
35975         for(var key in bp){
35976             if(typeof bp[key] != "function"){
35977                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
35978             }
35979         }
35980         var n = this.queryParam === false ? 'node' : this.queryParam;
35981         buf.push(n + "=", encodeURIComponent(node.id));
35982         return buf.join("");
35983     },
35984
35985     requestData : function(node, callback){
35986         if(this.fireEvent("beforeload", this, node, callback) !== false){
35987             this.transId = Roo.Ajax.request({
35988                 method:this.requestMethod,
35989                 url: this.dataUrl||this.url,
35990                 success: this.handleResponse,
35991                 failure: this.handleFailure,
35992                 scope: this,
35993                 argument: {callback: callback, node: node},
35994                 params: this.getParams(node)
35995             });
35996         }else{
35997             // if the load is cancelled, make sure we notify
35998             // the node that we are done
35999             if(typeof callback == "function"){
36000                 callback();
36001             }
36002         }
36003     },
36004
36005     isLoading : function(){
36006         return this.transId ? true : false;
36007     },
36008
36009     abort : function(){
36010         if(this.isLoading()){
36011             Roo.Ajax.abort(this.transId);
36012         }
36013     },
36014
36015     // private
36016     createNode : function(attr)
36017     {
36018         // apply baseAttrs, nice idea Corey!
36019         if(this.baseAttrs){
36020             Roo.applyIf(attr, this.baseAttrs);
36021         }
36022         if(this.applyLoader !== false){
36023             attr.loader = this;
36024         }
36025         // uiProvider = depreciated..
36026         
36027         if(typeof(attr.uiProvider) == 'string'){
36028            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36029                 /**  eval:var:attr */ eval(attr.uiProvider);
36030         }
36031         if(typeof(this.uiProviders['default']) != 'undefined') {
36032             attr.uiProvider = this.uiProviders['default'];
36033         }
36034         
36035         this.fireEvent('create', this, attr);
36036         
36037         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36038         return(attr.leaf ?
36039                         new Roo.tree.TreeNode(attr) :
36040                         new Roo.tree.AsyncTreeNode(attr));
36041     },
36042
36043     processResponse : function(response, node, callback)
36044     {
36045         var json = response.responseText;
36046         try {
36047             
36048             var o = Roo.decode(json);
36049             
36050             if (this.root === false && typeof(o.success) != undefined) {
36051                 this.root = 'data'; // the default behaviour for list like data..
36052                 }
36053                 
36054             if (this.root !== false &&  !o.success) {
36055                 // it's a failure condition.
36056                 var a = response.argument;
36057                 this.fireEvent("loadexception", this, a.node, response);
36058                 Roo.log("Load failed - should have a handler really");
36059                 return;
36060             }
36061             
36062             
36063             
36064             if (this.root !== false) {
36065                  o = o[this.root];
36066             }
36067             
36068             for(var i = 0, len = o.length; i < len; i++){
36069                 var n = this.createNode(o[i]);
36070                 if(n){
36071                     node.appendChild(n);
36072                 }
36073             }
36074             if(typeof callback == "function"){
36075                 callback(this, node);
36076             }
36077         }catch(e){
36078             this.handleFailure(response);
36079         }
36080     },
36081
36082     handleResponse : function(response){
36083         this.transId = false;
36084         var a = response.argument;
36085         this.processResponse(response, a.node, a.callback);
36086         this.fireEvent("load", this, a.node, response);
36087     },
36088
36089     handleFailure : function(response)
36090     {
36091         // should handle failure better..
36092         this.transId = false;
36093         var a = response.argument;
36094         this.fireEvent("loadexception", this, a.node, response);
36095         if(typeof a.callback == "function"){
36096             a.callback(this, a.node);
36097         }
36098     }
36099 });/*
36100  * Based on:
36101  * Ext JS Library 1.1.1
36102  * Copyright(c) 2006-2007, Ext JS, LLC.
36103  *
36104  * Originally Released Under LGPL - original licence link has changed is not relivant.
36105  *
36106  * Fork - LGPL
36107  * <script type="text/javascript">
36108  */
36109
36110 /**
36111 * @class Roo.tree.TreeFilter
36112 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36113 * @param {TreePanel} tree
36114 * @param {Object} config (optional)
36115  */
36116 Roo.tree.TreeFilter = function(tree, config){
36117     this.tree = tree;
36118     this.filtered = {};
36119     Roo.apply(this, config);
36120 };
36121
36122 Roo.tree.TreeFilter.prototype = {
36123     clearBlank:false,
36124     reverse:false,
36125     autoClear:false,
36126     remove:false,
36127
36128      /**
36129      * Filter the data by a specific attribute.
36130      * @param {String/RegExp} value Either string that the attribute value
36131      * should start with or a RegExp to test against the attribute
36132      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36133      * @param {TreeNode} startNode (optional) The node to start the filter at.
36134      */
36135     filter : function(value, attr, startNode){
36136         attr = attr || "text";
36137         var f;
36138         if(typeof value == "string"){
36139             var vlen = value.length;
36140             // auto clear empty filter
36141             if(vlen == 0 && this.clearBlank){
36142                 this.clear();
36143                 return;
36144             }
36145             value = value.toLowerCase();
36146             f = function(n){
36147                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36148             };
36149         }else if(value.exec){ // regex?
36150             f = function(n){
36151                 return value.test(n.attributes[attr]);
36152             };
36153         }else{
36154             throw 'Illegal filter type, must be string or regex';
36155         }
36156         this.filterBy(f, null, startNode);
36157         },
36158
36159     /**
36160      * Filter by a function. The passed function will be called with each
36161      * node in the tree (or from the startNode). If the function returns true, the node is kept
36162      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36163      * @param {Function} fn The filter function
36164      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36165      */
36166     filterBy : function(fn, scope, startNode){
36167         startNode = startNode || this.tree.root;
36168         if(this.autoClear){
36169             this.clear();
36170         }
36171         var af = this.filtered, rv = this.reverse;
36172         var f = function(n){
36173             if(n == startNode){
36174                 return true;
36175             }
36176             if(af[n.id]){
36177                 return false;
36178             }
36179             var m = fn.call(scope || n, n);
36180             if(!m || rv){
36181                 af[n.id] = n;
36182                 n.ui.hide();
36183                 return false;
36184             }
36185             return true;
36186         };
36187         startNode.cascade(f);
36188         if(this.remove){
36189            for(var id in af){
36190                if(typeof id != "function"){
36191                    var n = af[id];
36192                    if(n && n.parentNode){
36193                        n.parentNode.removeChild(n);
36194                    }
36195                }
36196            }
36197         }
36198     },
36199
36200     /**
36201      * Clears the current filter. Note: with the "remove" option
36202      * set a filter cannot be cleared.
36203      */
36204     clear : function(){
36205         var t = this.tree;
36206         var af = this.filtered;
36207         for(var id in af){
36208             if(typeof id != "function"){
36209                 var n = af[id];
36210                 if(n){
36211                     n.ui.show();
36212                 }
36213             }
36214         }
36215         this.filtered = {};
36216     }
36217 };
36218 /*
36219  * Based on:
36220  * Ext JS Library 1.1.1
36221  * Copyright(c) 2006-2007, Ext JS, LLC.
36222  *
36223  * Originally Released Under LGPL - original licence link has changed is not relivant.
36224  *
36225  * Fork - LGPL
36226  * <script type="text/javascript">
36227  */
36228  
36229
36230 /**
36231  * @class Roo.tree.TreeSorter
36232  * Provides sorting of nodes in a TreePanel
36233  * 
36234  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36235  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36236  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36237  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36238  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36239  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36240  * @constructor
36241  * @param {TreePanel} tree
36242  * @param {Object} config
36243  */
36244 Roo.tree.TreeSorter = function(tree, config){
36245     Roo.apply(this, config);
36246     tree.on("beforechildrenrendered", this.doSort, this);
36247     tree.on("append", this.updateSort, this);
36248     tree.on("insert", this.updateSort, this);
36249     
36250     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36251     var p = this.property || "text";
36252     var sortType = this.sortType;
36253     var fs = this.folderSort;
36254     var cs = this.caseSensitive === true;
36255     var leafAttr = this.leafAttr || 'leaf';
36256
36257     this.sortFn = function(n1, n2){
36258         if(fs){
36259             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36260                 return 1;
36261             }
36262             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36263                 return -1;
36264             }
36265         }
36266         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36267         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36268         if(v1 < v2){
36269                         return dsc ? +1 : -1;
36270                 }else if(v1 > v2){
36271                         return dsc ? -1 : +1;
36272         }else{
36273                 return 0;
36274         }
36275     };
36276 };
36277
36278 Roo.tree.TreeSorter.prototype = {
36279     doSort : function(node){
36280         node.sort(this.sortFn);
36281     },
36282     
36283     compareNodes : function(n1, n2){
36284         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36285     },
36286     
36287     updateSort : function(tree, node){
36288         if(node.childrenRendered){
36289             this.doSort.defer(1, this, [node]);
36290         }
36291     }
36292 };/*
36293  * Based on:
36294  * Ext JS Library 1.1.1
36295  * Copyright(c) 2006-2007, Ext JS, LLC.
36296  *
36297  * Originally Released Under LGPL - original licence link has changed is not relivant.
36298  *
36299  * Fork - LGPL
36300  * <script type="text/javascript">
36301  */
36302
36303 if(Roo.dd.DropZone){
36304     
36305 Roo.tree.TreeDropZone = function(tree, config){
36306     this.allowParentInsert = false;
36307     this.allowContainerDrop = false;
36308     this.appendOnly = false;
36309     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36310     this.tree = tree;
36311     this.lastInsertClass = "x-tree-no-status";
36312     this.dragOverData = {};
36313 };
36314
36315 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36316     ddGroup : "TreeDD",
36317     scroll:  true,
36318     
36319     expandDelay : 1000,
36320     
36321     expandNode : function(node){
36322         if(node.hasChildNodes() && !node.isExpanded()){
36323             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36324         }
36325     },
36326     
36327     queueExpand : function(node){
36328         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36329     },
36330     
36331     cancelExpand : function(){
36332         if(this.expandProcId){
36333             clearTimeout(this.expandProcId);
36334             this.expandProcId = false;
36335         }
36336     },
36337     
36338     isValidDropPoint : function(n, pt, dd, e, data){
36339         if(!n || !data){ return false; }
36340         var targetNode = n.node;
36341         var dropNode = data.node;
36342         // default drop rules
36343         if(!(targetNode && targetNode.isTarget && pt)){
36344             return false;
36345         }
36346         if(pt == "append" && targetNode.allowChildren === false){
36347             return false;
36348         }
36349         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36350             return false;
36351         }
36352         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36353             return false;
36354         }
36355         // reuse the object
36356         var overEvent = this.dragOverData;
36357         overEvent.tree = this.tree;
36358         overEvent.target = targetNode;
36359         overEvent.data = data;
36360         overEvent.point = pt;
36361         overEvent.source = dd;
36362         overEvent.rawEvent = e;
36363         overEvent.dropNode = dropNode;
36364         overEvent.cancel = false;  
36365         var result = this.tree.fireEvent("nodedragover", overEvent);
36366         return overEvent.cancel === false && result !== false;
36367     },
36368     
36369     getDropPoint : function(e, n, dd)
36370     {
36371         var tn = n.node;
36372         if(tn.isRoot){
36373             return tn.allowChildren !== false ? "append" : false; // always append for root
36374         }
36375         var dragEl = n.ddel;
36376         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36377         var y = Roo.lib.Event.getPageY(e);
36378         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36379         
36380         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36381         var noAppend = tn.allowChildren === false;
36382         if(this.appendOnly || tn.parentNode.allowChildren === false){
36383             return noAppend ? false : "append";
36384         }
36385         var noBelow = false;
36386         if(!this.allowParentInsert){
36387             noBelow = tn.hasChildNodes() && tn.isExpanded();
36388         }
36389         var q = (b - t) / (noAppend ? 2 : 3);
36390         if(y >= t && y < (t + q)){
36391             return "above";
36392         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36393             return "below";
36394         }else{
36395             return "append";
36396         }
36397     },
36398     
36399     onNodeEnter : function(n, dd, e, data)
36400     {
36401         this.cancelExpand();
36402     },
36403     
36404     onNodeOver : function(n, dd, e, data)
36405     {
36406        
36407         var pt = this.getDropPoint(e, n, dd);
36408         var node = n.node;
36409         
36410         // auto node expand check
36411         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36412             this.queueExpand(node);
36413         }else if(pt != "append"){
36414             this.cancelExpand();
36415         }
36416         
36417         // set the insert point style on the target node
36418         var returnCls = this.dropNotAllowed;
36419         if(this.isValidDropPoint(n, pt, dd, e, data)){
36420            if(pt){
36421                var el = n.ddel;
36422                var cls;
36423                if(pt == "above"){
36424                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36425                    cls = "x-tree-drag-insert-above";
36426                }else if(pt == "below"){
36427                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36428                    cls = "x-tree-drag-insert-below";
36429                }else{
36430                    returnCls = "x-tree-drop-ok-append";
36431                    cls = "x-tree-drag-append";
36432                }
36433                if(this.lastInsertClass != cls){
36434                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36435                    this.lastInsertClass = cls;
36436                }
36437            }
36438        }
36439        return returnCls;
36440     },
36441     
36442     onNodeOut : function(n, dd, e, data){
36443         
36444         this.cancelExpand();
36445         this.removeDropIndicators(n);
36446     },
36447     
36448     onNodeDrop : function(n, dd, e, data){
36449         var point = this.getDropPoint(e, n, dd);
36450         var targetNode = n.node;
36451         targetNode.ui.startDrop();
36452         if(!this.isValidDropPoint(n, point, dd, e, data)){
36453             targetNode.ui.endDrop();
36454             return false;
36455         }
36456         // first try to find the drop node
36457         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36458         var dropEvent = {
36459             tree : this.tree,
36460             target: targetNode,
36461             data: data,
36462             point: point,
36463             source: dd,
36464             rawEvent: e,
36465             dropNode: dropNode,
36466             cancel: !dropNode   
36467         };
36468         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36469         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36470             targetNode.ui.endDrop();
36471             return false;
36472         }
36473         // allow target changing
36474         targetNode = dropEvent.target;
36475         if(point == "append" && !targetNode.isExpanded()){
36476             targetNode.expand(false, null, function(){
36477                 this.completeDrop(dropEvent);
36478             }.createDelegate(this));
36479         }else{
36480             this.completeDrop(dropEvent);
36481         }
36482         return true;
36483     },
36484     
36485     completeDrop : function(de){
36486         var ns = de.dropNode, p = de.point, t = de.target;
36487         if(!(ns instanceof Array)){
36488             ns = [ns];
36489         }
36490         var n;
36491         for(var i = 0, len = ns.length; i < len; i++){
36492             n = ns[i];
36493             if(p == "above"){
36494                 t.parentNode.insertBefore(n, t);
36495             }else if(p == "below"){
36496                 t.parentNode.insertBefore(n, t.nextSibling);
36497             }else{
36498                 t.appendChild(n);
36499             }
36500         }
36501         n.ui.focus();
36502         if(this.tree.hlDrop){
36503             n.ui.highlight();
36504         }
36505         t.ui.endDrop();
36506         this.tree.fireEvent("nodedrop", de);
36507     },
36508     
36509     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36510         if(this.tree.hlDrop){
36511             dropNode.ui.focus();
36512             dropNode.ui.highlight();
36513         }
36514         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36515     },
36516     
36517     getTree : function(){
36518         return this.tree;
36519     },
36520     
36521     removeDropIndicators : function(n){
36522         if(n && n.ddel){
36523             var el = n.ddel;
36524             Roo.fly(el).removeClass([
36525                     "x-tree-drag-insert-above",
36526                     "x-tree-drag-insert-below",
36527                     "x-tree-drag-append"]);
36528             this.lastInsertClass = "_noclass";
36529         }
36530     },
36531     
36532     beforeDragDrop : function(target, e, id){
36533         this.cancelExpand();
36534         return true;
36535     },
36536     
36537     afterRepair : function(data){
36538         if(data && Roo.enableFx){
36539             data.node.ui.highlight();
36540         }
36541         this.hideProxy();
36542     } 
36543     
36544 });
36545
36546 }
36547 /*
36548  * Based on:
36549  * Ext JS Library 1.1.1
36550  * Copyright(c) 2006-2007, Ext JS, LLC.
36551  *
36552  * Originally Released Under LGPL - original licence link has changed is not relivant.
36553  *
36554  * Fork - LGPL
36555  * <script type="text/javascript">
36556  */
36557  
36558
36559 if(Roo.dd.DragZone){
36560 Roo.tree.TreeDragZone = function(tree, config){
36561     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36562     this.tree = tree;
36563 };
36564
36565 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36566     ddGroup : "TreeDD",
36567    
36568     onBeforeDrag : function(data, e){
36569         var n = data.node;
36570         return n && n.draggable && !n.disabled;
36571     },
36572      
36573     
36574     onInitDrag : function(e){
36575         var data = this.dragData;
36576         this.tree.getSelectionModel().select(data.node);
36577         this.proxy.update("");
36578         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36579         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36580     },
36581     
36582     getRepairXY : function(e, data){
36583         return data.node.ui.getDDRepairXY();
36584     },
36585     
36586     onEndDrag : function(data, e){
36587         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36588         
36589         
36590     },
36591     
36592     onValidDrop : function(dd, e, id){
36593         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36594         this.hideProxy();
36595     },
36596     
36597     beforeInvalidDrop : function(e, id){
36598         // this scrolls the original position back into view
36599         var sm = this.tree.getSelectionModel();
36600         sm.clearSelections();
36601         sm.select(this.dragData.node);
36602     }
36603 });
36604 }/*
36605  * Based on:
36606  * Ext JS Library 1.1.1
36607  * Copyright(c) 2006-2007, Ext JS, LLC.
36608  *
36609  * Originally Released Under LGPL - original licence link has changed is not relivant.
36610  *
36611  * Fork - LGPL
36612  * <script type="text/javascript">
36613  */
36614 /**
36615  * @class Roo.tree.TreeEditor
36616  * @extends Roo.Editor
36617  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36618  * as the editor field.
36619  * @constructor
36620  * @param {Object} config (used to be the tree panel.)
36621  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36622  * 
36623  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36624  * @cfg {Roo.form.TextField|Object} field The field configuration
36625  *
36626  * 
36627  */
36628 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36629     var tree = config;
36630     var field;
36631     if (oldconfig) { // old style..
36632         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36633     } else {
36634         // new style..
36635         tree = config.tree;
36636         config.field = config.field  || {};
36637         config.field.xtype = 'TextField';
36638         field = Roo.factory(config.field, Roo.form);
36639     }
36640     config = config || {};
36641     
36642     
36643     this.addEvents({
36644         /**
36645          * @event beforenodeedit
36646          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36647          * false from the handler of this event.
36648          * @param {Editor} this
36649          * @param {Roo.tree.Node} node 
36650          */
36651         "beforenodeedit" : true
36652     });
36653     
36654     //Roo.log(config);
36655     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36656
36657     this.tree = tree;
36658
36659     tree.on('beforeclick', this.beforeNodeClick, this);
36660     tree.getTreeEl().on('mousedown', this.hide, this);
36661     this.on('complete', this.updateNode, this);
36662     this.on('beforestartedit', this.fitToTree, this);
36663     this.on('startedit', this.bindScroll, this, {delay:10});
36664     this.on('specialkey', this.onSpecialKey, this);
36665 };
36666
36667 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36668     /**
36669      * @cfg {String} alignment
36670      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36671      */
36672     alignment: "l-l",
36673     // inherit
36674     autoSize: false,
36675     /**
36676      * @cfg {Boolean} hideEl
36677      * True to hide the bound element while the editor is displayed (defaults to false)
36678      */
36679     hideEl : false,
36680     /**
36681      * @cfg {String} cls
36682      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36683      */
36684     cls: "x-small-editor x-tree-editor",
36685     /**
36686      * @cfg {Boolean} shim
36687      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36688      */
36689     shim:false,
36690     // inherit
36691     shadow:"frame",
36692     /**
36693      * @cfg {Number} maxWidth
36694      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36695      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36696      * scroll and client offsets into account prior to each edit.
36697      */
36698     maxWidth: 250,
36699
36700     editDelay : 350,
36701
36702     // private
36703     fitToTree : function(ed, el){
36704         var td = this.tree.getTreeEl().dom, nd = el.dom;
36705         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36706             td.scrollLeft = nd.offsetLeft;
36707         }
36708         var w = Math.min(
36709                 this.maxWidth,
36710                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36711         this.setSize(w, '');
36712         
36713         return this.fireEvent('beforenodeedit', this, this.editNode);
36714         
36715     },
36716
36717     // private
36718     triggerEdit : function(node){
36719         this.completeEdit();
36720         this.editNode = node;
36721         this.startEdit(node.ui.textNode, node.text);
36722     },
36723
36724     // private
36725     bindScroll : function(){
36726         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36727     },
36728
36729     // private
36730     beforeNodeClick : function(node, e){
36731         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36732         this.lastClick = new Date();
36733         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36734             e.stopEvent();
36735             this.triggerEdit(node);
36736             return false;
36737         }
36738         return true;
36739     },
36740
36741     // private
36742     updateNode : function(ed, value){
36743         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36744         this.editNode.setText(value);
36745     },
36746
36747     // private
36748     onHide : function(){
36749         Roo.tree.TreeEditor.superclass.onHide.call(this);
36750         if(this.editNode){
36751             this.editNode.ui.focus();
36752         }
36753     },
36754
36755     // private
36756     onSpecialKey : function(field, e){
36757         var k = e.getKey();
36758         if(k == e.ESC){
36759             e.stopEvent();
36760             this.cancelEdit();
36761         }else if(k == e.ENTER && !e.hasModifier()){
36762             e.stopEvent();
36763             this.completeEdit();
36764         }
36765     }
36766 });//<Script type="text/javascript">
36767 /*
36768  * Based on:
36769  * Ext JS Library 1.1.1
36770  * Copyright(c) 2006-2007, Ext JS, LLC.
36771  *
36772  * Originally Released Under LGPL - original licence link has changed is not relivant.
36773  *
36774  * Fork - LGPL
36775  * <script type="text/javascript">
36776  */
36777  
36778 /**
36779  * Not documented??? - probably should be...
36780  */
36781
36782 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36783     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36784     
36785     renderElements : function(n, a, targetNode, bulkRender){
36786         //consel.log("renderElements?");
36787         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36788
36789         var t = n.getOwnerTree();
36790         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36791         
36792         var cols = t.columns;
36793         var bw = t.borderWidth;
36794         var c = cols[0];
36795         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36796          var cb = typeof a.checked == "boolean";
36797         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36798         var colcls = 'x-t-' + tid + '-c0';
36799         var buf = [
36800             '<li class="x-tree-node">',
36801             
36802                 
36803                 '<div class="x-tree-node-el ', a.cls,'">',
36804                     // extran...
36805                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36806                 
36807                 
36808                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36809                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36810                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36811                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36812                            (a.iconCls ? ' '+a.iconCls : ''),
36813                            '" unselectable="on" />',
36814                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36815                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36816                              
36817                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36818                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36819                             '<span unselectable="on" qtip="' + tx + '">',
36820                              tx,
36821                              '</span></a>' ,
36822                     '</div>',
36823                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36824                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36825                  ];
36826         for(var i = 1, len = cols.length; i < len; i++){
36827             c = cols[i];
36828             colcls = 'x-t-' + tid + '-c' +i;
36829             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36830             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36831                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36832                       "</div>");
36833          }
36834          
36835          buf.push(
36836             '</a>',
36837             '<div class="x-clear"></div></div>',
36838             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36839             "</li>");
36840         
36841         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36842             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36843                                 n.nextSibling.ui.getEl(), buf.join(""));
36844         }else{
36845             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36846         }
36847         var el = this.wrap.firstChild;
36848         this.elRow = el;
36849         this.elNode = el.firstChild;
36850         this.ranchor = el.childNodes[1];
36851         this.ctNode = this.wrap.childNodes[1];
36852         var cs = el.firstChild.childNodes;
36853         this.indentNode = cs[0];
36854         this.ecNode = cs[1];
36855         this.iconNode = cs[2];
36856         var index = 3;
36857         if(cb){
36858             this.checkbox = cs[3];
36859             index++;
36860         }
36861         this.anchor = cs[index];
36862         
36863         this.textNode = cs[index].firstChild;
36864         
36865         //el.on("click", this.onClick, this);
36866         //el.on("dblclick", this.onDblClick, this);
36867         
36868         
36869        // console.log(this);
36870     },
36871     initEvents : function(){
36872         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36873         
36874             
36875         var a = this.ranchor;
36876
36877         var el = Roo.get(a);
36878
36879         if(Roo.isOpera){ // opera render bug ignores the CSS
36880             el.setStyle("text-decoration", "none");
36881         }
36882
36883         el.on("click", this.onClick, this);
36884         el.on("dblclick", this.onDblClick, this);
36885         el.on("contextmenu", this.onContextMenu, this);
36886         
36887     },
36888     
36889     /*onSelectedChange : function(state){
36890         if(state){
36891             this.focus();
36892             this.addClass("x-tree-selected");
36893         }else{
36894             //this.blur();
36895             this.removeClass("x-tree-selected");
36896         }
36897     },*/
36898     addClass : function(cls){
36899         if(this.elRow){
36900             Roo.fly(this.elRow).addClass(cls);
36901         }
36902         
36903     },
36904     
36905     
36906     removeClass : function(cls){
36907         if(this.elRow){
36908             Roo.fly(this.elRow).removeClass(cls);
36909         }
36910     }
36911
36912     
36913     
36914 });//<Script type="text/javascript">
36915
36916 /*
36917  * Based on:
36918  * Ext JS Library 1.1.1
36919  * Copyright(c) 2006-2007, Ext JS, LLC.
36920  *
36921  * Originally Released Under LGPL - original licence link has changed is not relivant.
36922  *
36923  * Fork - LGPL
36924  * <script type="text/javascript">
36925  */
36926  
36927
36928 /**
36929  * @class Roo.tree.ColumnTree
36930  * @extends Roo.data.TreePanel
36931  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36932  * @cfg {int} borderWidth  compined right/left border allowance
36933  * @constructor
36934  * @param {String/HTMLElement/Element} el The container element
36935  * @param {Object} config
36936  */
36937 Roo.tree.ColumnTree =  function(el, config)
36938 {
36939    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36940    this.addEvents({
36941         /**
36942         * @event resize
36943         * Fire this event on a container when it resizes
36944         * @param {int} w Width
36945         * @param {int} h Height
36946         */
36947        "resize" : true
36948     });
36949     this.on('resize', this.onResize, this);
36950 };
36951
36952 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36953     //lines:false,
36954     
36955     
36956     borderWidth: Roo.isBorderBox ? 0 : 2, 
36957     headEls : false,
36958     
36959     render : function(){
36960         // add the header.....
36961        
36962         Roo.tree.ColumnTree.superclass.render.apply(this);
36963         
36964         this.el.addClass('x-column-tree');
36965         
36966         this.headers = this.el.createChild(
36967             {cls:'x-tree-headers'},this.innerCt.dom);
36968    
36969         var cols = this.columns, c;
36970         var totalWidth = 0;
36971         this.headEls = [];
36972         var  len = cols.length;
36973         for(var i = 0; i < len; i++){
36974              c = cols[i];
36975              totalWidth += c.width;
36976             this.headEls.push(this.headers.createChild({
36977                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
36978                  cn: {
36979                      cls:'x-tree-hd-text',
36980                      html: c.header
36981                  },
36982                  style:'width:'+(c.width-this.borderWidth)+'px;'
36983              }));
36984         }
36985         this.headers.createChild({cls:'x-clear'});
36986         // prevent floats from wrapping when clipped
36987         this.headers.setWidth(totalWidth);
36988         //this.innerCt.setWidth(totalWidth);
36989         this.innerCt.setStyle({ overflow: 'auto' });
36990         this.onResize(this.width, this.height);
36991              
36992         
36993     },
36994     onResize : function(w,h)
36995     {
36996         this.height = h;
36997         this.width = w;
36998         // resize cols..
36999         this.innerCt.setWidth(this.width);
37000         this.innerCt.setHeight(this.height-20);
37001         
37002         // headers...
37003         var cols = this.columns, c;
37004         var totalWidth = 0;
37005         var expEl = false;
37006         var len = cols.length;
37007         for(var i = 0; i < len; i++){
37008             c = cols[i];
37009             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37010                 // it's the expander..
37011                 expEl  = this.headEls[i];
37012                 continue;
37013             }
37014             totalWidth += c.width;
37015             
37016         }
37017         if (expEl) {
37018             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37019         }
37020         this.headers.setWidth(w-20);
37021
37022         
37023         
37024         
37025     }
37026 });
37027 /*
37028  * Based on:
37029  * Ext JS Library 1.1.1
37030  * Copyright(c) 2006-2007, Ext JS, LLC.
37031  *
37032  * Originally Released Under LGPL - original licence link has changed is not relivant.
37033  *
37034  * Fork - LGPL
37035  * <script type="text/javascript">
37036  */
37037  
37038 /**
37039  * @class Roo.menu.Menu
37040  * @extends Roo.util.Observable
37041  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37042  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37043  * @constructor
37044  * Creates a new Menu
37045  * @param {Object} config Configuration options
37046  */
37047 Roo.menu.Menu = function(config){
37048     Roo.apply(this, config);
37049     this.id = this.id || Roo.id();
37050     this.addEvents({
37051         /**
37052          * @event beforeshow
37053          * Fires before this menu is displayed
37054          * @param {Roo.menu.Menu} this
37055          */
37056         beforeshow : true,
37057         /**
37058          * @event beforehide
37059          * Fires before this menu is hidden
37060          * @param {Roo.menu.Menu} this
37061          */
37062         beforehide : true,
37063         /**
37064          * @event show
37065          * Fires after this menu is displayed
37066          * @param {Roo.menu.Menu} this
37067          */
37068         show : true,
37069         /**
37070          * @event hide
37071          * Fires after this menu is hidden
37072          * @param {Roo.menu.Menu} this
37073          */
37074         hide : true,
37075         /**
37076          * @event click
37077          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37078          * @param {Roo.menu.Menu} this
37079          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37080          * @param {Roo.EventObject} e
37081          */
37082         click : true,
37083         /**
37084          * @event mouseover
37085          * Fires when the mouse is hovering over this menu
37086          * @param {Roo.menu.Menu} this
37087          * @param {Roo.EventObject} e
37088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37089          */
37090         mouseover : true,
37091         /**
37092          * @event mouseout
37093          * Fires when the mouse exits this menu
37094          * @param {Roo.menu.Menu} this
37095          * @param {Roo.EventObject} e
37096          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37097          */
37098         mouseout : true,
37099         /**
37100          * @event itemclick
37101          * Fires when a menu item contained in this menu is clicked
37102          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37103          * @param {Roo.EventObject} e
37104          */
37105         itemclick: true
37106     });
37107     if (this.registerMenu) {
37108         Roo.menu.MenuMgr.register(this);
37109     }
37110     
37111     var mis = this.items;
37112     this.items = new Roo.util.MixedCollection();
37113     if(mis){
37114         this.add.apply(this, mis);
37115     }
37116 };
37117
37118 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37119     /**
37120      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37121      */
37122     minWidth : 120,
37123     /**
37124      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37125      * for bottom-right shadow (defaults to "sides")
37126      */
37127     shadow : "sides",
37128     /**
37129      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37130      * this menu (defaults to "tl-tr?")
37131      */
37132     subMenuAlign : "tl-tr?",
37133     /**
37134      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37135      * relative to its element of origin (defaults to "tl-bl?")
37136      */
37137     defaultAlign : "tl-bl?",
37138     /**
37139      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37140      */
37141     allowOtherMenus : false,
37142     /**
37143      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37144      */
37145     registerMenu : true,
37146
37147     hidden:true,
37148
37149     // private
37150     render : function(){
37151         if(this.el){
37152             return;
37153         }
37154         var el = this.el = new Roo.Layer({
37155             cls: "x-menu",
37156             shadow:this.shadow,
37157             constrain: false,
37158             parentEl: this.parentEl || document.body,
37159             zindex:15000
37160         });
37161
37162         this.keyNav = new Roo.menu.MenuNav(this);
37163
37164         if(this.plain){
37165             el.addClass("x-menu-plain");
37166         }
37167         if(this.cls){
37168             el.addClass(this.cls);
37169         }
37170         // generic focus element
37171         this.focusEl = el.createChild({
37172             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37173         });
37174         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37175         //disabling touch- as it's causing issues ..
37176         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37177         ul.on('click'   , this.onClick, this);
37178         
37179         
37180         ul.on("mouseover", this.onMouseOver, this);
37181         ul.on("mouseout", this.onMouseOut, this);
37182         this.items.each(function(item){
37183             if (item.hidden) {
37184                 return;
37185             }
37186             
37187             var li = document.createElement("li");
37188             li.className = "x-menu-list-item";
37189             ul.dom.appendChild(li);
37190             item.render(li, this);
37191         }, this);
37192         this.ul = ul;
37193         this.autoWidth();
37194     },
37195
37196     // private
37197     autoWidth : function(){
37198         var el = this.el, ul = this.ul;
37199         if(!el){
37200             return;
37201         }
37202         var w = this.width;
37203         if(w){
37204             el.setWidth(w);
37205         }else if(Roo.isIE){
37206             el.setWidth(this.minWidth);
37207             var t = el.dom.offsetWidth; // force recalc
37208             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37209         }
37210     },
37211
37212     // private
37213     delayAutoWidth : function(){
37214         if(this.rendered){
37215             if(!this.awTask){
37216                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37217             }
37218             this.awTask.delay(20);
37219         }
37220     },
37221
37222     // private
37223     findTargetItem : function(e){
37224         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37225         if(t && t.menuItemId){
37226             return this.items.get(t.menuItemId);
37227         }
37228     },
37229
37230     // private
37231     onClick : function(e){
37232         Roo.log("menu.onClick");
37233         var t = this.findTargetItem(e);
37234         if(!t){
37235             return;
37236         }
37237         Roo.log(e);
37238         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37239             if(t == this.activeItem && t.shouldDeactivate(e)){
37240                 this.activeItem.deactivate();
37241                 delete this.activeItem;
37242                 return;
37243             }
37244             if(t.canActivate){
37245                 this.setActiveItem(t, true);
37246             }
37247             return;
37248             
37249             
37250         }
37251         
37252         t.onClick(e);
37253         this.fireEvent("click", this, t, e);
37254     },
37255
37256     // private
37257     setActiveItem : function(item, autoExpand){
37258         if(item != this.activeItem){
37259             if(this.activeItem){
37260                 this.activeItem.deactivate();
37261             }
37262             this.activeItem = item;
37263             item.activate(autoExpand);
37264         }else if(autoExpand){
37265             item.expandMenu();
37266         }
37267     },
37268
37269     // private
37270     tryActivate : function(start, step){
37271         var items = this.items;
37272         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37273             var item = items.get(i);
37274             if(!item.disabled && item.canActivate){
37275                 this.setActiveItem(item, false);
37276                 return item;
37277             }
37278         }
37279         return false;
37280     },
37281
37282     // private
37283     onMouseOver : function(e){
37284         var t;
37285         if(t = this.findTargetItem(e)){
37286             if(t.canActivate && !t.disabled){
37287                 this.setActiveItem(t, true);
37288             }
37289         }
37290         this.fireEvent("mouseover", this, e, t);
37291     },
37292
37293     // private
37294     onMouseOut : function(e){
37295         var t;
37296         if(t = this.findTargetItem(e)){
37297             if(t == this.activeItem && t.shouldDeactivate(e)){
37298                 this.activeItem.deactivate();
37299                 delete this.activeItem;
37300             }
37301         }
37302         this.fireEvent("mouseout", this, e, t);
37303     },
37304
37305     /**
37306      * Read-only.  Returns true if the menu is currently displayed, else false.
37307      * @type Boolean
37308      */
37309     isVisible : function(){
37310         return this.el && !this.hidden;
37311     },
37312
37313     /**
37314      * Displays this menu relative to another element
37315      * @param {String/HTMLElement/Roo.Element} element The element to align to
37316      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37317      * the element (defaults to this.defaultAlign)
37318      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37319      */
37320     show : function(el, pos, parentMenu){
37321         this.parentMenu = parentMenu;
37322         if(!this.el){
37323             this.render();
37324         }
37325         this.fireEvent("beforeshow", this);
37326         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37327     },
37328
37329     /**
37330      * Displays this menu at a specific xy position
37331      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37332      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37333      */
37334     showAt : function(xy, parentMenu, /* private: */_e){
37335         this.parentMenu = parentMenu;
37336         if(!this.el){
37337             this.render();
37338         }
37339         if(_e !== false){
37340             this.fireEvent("beforeshow", this);
37341             xy = this.el.adjustForConstraints(xy);
37342         }
37343         this.el.setXY(xy);
37344         this.el.show();
37345         this.hidden = false;
37346         this.focus();
37347         this.fireEvent("show", this);
37348     },
37349
37350     focus : function(){
37351         if(!this.hidden){
37352             this.doFocus.defer(50, this);
37353         }
37354     },
37355
37356     doFocus : function(){
37357         if(!this.hidden){
37358             this.focusEl.focus();
37359         }
37360     },
37361
37362     /**
37363      * Hides this menu and optionally all parent menus
37364      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37365      */
37366     hide : function(deep){
37367         if(this.el && this.isVisible()){
37368             this.fireEvent("beforehide", this);
37369             if(this.activeItem){
37370                 this.activeItem.deactivate();
37371                 this.activeItem = null;
37372             }
37373             this.el.hide();
37374             this.hidden = true;
37375             this.fireEvent("hide", this);
37376         }
37377         if(deep === true && this.parentMenu){
37378             this.parentMenu.hide(true);
37379         }
37380     },
37381
37382     /**
37383      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37384      * Any of the following are valid:
37385      * <ul>
37386      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37387      * <li>An HTMLElement object which will be converted to a menu item</li>
37388      * <li>A menu item config object that will be created as a new menu item</li>
37389      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37390      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37391      * </ul>
37392      * Usage:
37393      * <pre><code>
37394 // Create the menu
37395 var menu = new Roo.menu.Menu();
37396
37397 // Create a menu item to add by reference
37398 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37399
37400 // Add a bunch of items at once using different methods.
37401 // Only the last item added will be returned.
37402 var item = menu.add(
37403     menuItem,                // add existing item by ref
37404     'Dynamic Item',          // new TextItem
37405     '-',                     // new separator
37406     { text: 'Config Item' }  // new item by config
37407 );
37408 </code></pre>
37409      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37410      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37411      */
37412     add : function(){
37413         var a = arguments, l = a.length, item;
37414         for(var i = 0; i < l; i++){
37415             var el = a[i];
37416             if ((typeof(el) == "object") && el.xtype && el.xns) {
37417                 el = Roo.factory(el, Roo.menu);
37418             }
37419             
37420             if(el.render){ // some kind of Item
37421                 item = this.addItem(el);
37422             }else if(typeof el == "string"){ // string
37423                 if(el == "separator" || el == "-"){
37424                     item = this.addSeparator();
37425                 }else{
37426                     item = this.addText(el);
37427                 }
37428             }else if(el.tagName || el.el){ // element
37429                 item = this.addElement(el);
37430             }else if(typeof el == "object"){ // must be menu item config?
37431                 item = this.addMenuItem(el);
37432             }
37433         }
37434         return item;
37435     },
37436
37437     /**
37438      * Returns this menu's underlying {@link Roo.Element} object
37439      * @return {Roo.Element} The element
37440      */
37441     getEl : function(){
37442         if(!this.el){
37443             this.render();
37444         }
37445         return this.el;
37446     },
37447
37448     /**
37449      * Adds a separator bar to the menu
37450      * @return {Roo.menu.Item} The menu item that was added
37451      */
37452     addSeparator : function(){
37453         return this.addItem(new Roo.menu.Separator());
37454     },
37455
37456     /**
37457      * Adds an {@link Roo.Element} object to the menu
37458      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37459      * @return {Roo.menu.Item} The menu item that was added
37460      */
37461     addElement : function(el){
37462         return this.addItem(new Roo.menu.BaseItem(el));
37463     },
37464
37465     /**
37466      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37467      * @param {Roo.menu.Item} item The menu item to add
37468      * @return {Roo.menu.Item} The menu item that was added
37469      */
37470     addItem : function(item){
37471         this.items.add(item);
37472         if(this.ul){
37473             var li = document.createElement("li");
37474             li.className = "x-menu-list-item";
37475             this.ul.dom.appendChild(li);
37476             item.render(li, this);
37477             this.delayAutoWidth();
37478         }
37479         return item;
37480     },
37481
37482     /**
37483      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37484      * @param {Object} config A MenuItem config object
37485      * @return {Roo.menu.Item} The menu item that was added
37486      */
37487     addMenuItem : function(config){
37488         if(!(config instanceof Roo.menu.Item)){
37489             if(typeof config.checked == "boolean"){ // must be check menu item config?
37490                 config = new Roo.menu.CheckItem(config);
37491             }else{
37492                 config = new Roo.menu.Item(config);
37493             }
37494         }
37495         return this.addItem(config);
37496     },
37497
37498     /**
37499      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37500      * @param {String} text The text to display in the menu item
37501      * @return {Roo.menu.Item} The menu item that was added
37502      */
37503     addText : function(text){
37504         return this.addItem(new Roo.menu.TextItem({ text : text }));
37505     },
37506
37507     /**
37508      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37509      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37510      * @param {Roo.menu.Item} item The menu item to add
37511      * @return {Roo.menu.Item} The menu item that was added
37512      */
37513     insert : function(index, item){
37514         this.items.insert(index, item);
37515         if(this.ul){
37516             var li = document.createElement("li");
37517             li.className = "x-menu-list-item";
37518             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37519             item.render(li, this);
37520             this.delayAutoWidth();
37521         }
37522         return item;
37523     },
37524
37525     /**
37526      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37527      * @param {Roo.menu.Item} item The menu item to remove
37528      */
37529     remove : function(item){
37530         this.items.removeKey(item.id);
37531         item.destroy();
37532     },
37533
37534     /**
37535      * Removes and destroys all items in the menu
37536      */
37537     removeAll : function(){
37538         var f;
37539         while(f = this.items.first()){
37540             this.remove(f);
37541         }
37542     }
37543 });
37544
37545 // MenuNav is a private utility class used internally by the Menu
37546 Roo.menu.MenuNav = function(menu){
37547     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37548     this.scope = this.menu = menu;
37549 };
37550
37551 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37552     doRelay : function(e, h){
37553         var k = e.getKey();
37554         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37555             this.menu.tryActivate(0, 1);
37556             return false;
37557         }
37558         return h.call(this.scope || this, e, this.menu);
37559     },
37560
37561     up : function(e, m){
37562         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37563             m.tryActivate(m.items.length-1, -1);
37564         }
37565     },
37566
37567     down : function(e, m){
37568         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37569             m.tryActivate(0, 1);
37570         }
37571     },
37572
37573     right : function(e, m){
37574         if(m.activeItem){
37575             m.activeItem.expandMenu(true);
37576         }
37577     },
37578
37579     left : function(e, m){
37580         m.hide();
37581         if(m.parentMenu && m.parentMenu.activeItem){
37582             m.parentMenu.activeItem.activate();
37583         }
37584     },
37585
37586     enter : function(e, m){
37587         if(m.activeItem){
37588             e.stopPropagation();
37589             m.activeItem.onClick(e);
37590             m.fireEvent("click", this, m.activeItem);
37591             return true;
37592         }
37593     }
37594 });/*
37595  * Based on:
37596  * Ext JS Library 1.1.1
37597  * Copyright(c) 2006-2007, Ext JS, LLC.
37598  *
37599  * Originally Released Under LGPL - original licence link has changed is not relivant.
37600  *
37601  * Fork - LGPL
37602  * <script type="text/javascript">
37603  */
37604  
37605 /**
37606  * @class Roo.menu.MenuMgr
37607  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37608  * @singleton
37609  */
37610 Roo.menu.MenuMgr = function(){
37611    var menus, active, groups = {}, attached = false, lastShow = new Date();
37612
37613    // private - called when first menu is created
37614    function init(){
37615        menus = {};
37616        active = new Roo.util.MixedCollection();
37617        Roo.get(document).addKeyListener(27, function(){
37618            if(active.length > 0){
37619                hideAll();
37620            }
37621        });
37622    }
37623
37624    // private
37625    function hideAll(){
37626        if(active && active.length > 0){
37627            var c = active.clone();
37628            c.each(function(m){
37629                m.hide();
37630            });
37631        }
37632    }
37633
37634    // private
37635    function onHide(m){
37636        active.remove(m);
37637        if(active.length < 1){
37638            Roo.get(document).un("mousedown", onMouseDown);
37639            attached = false;
37640        }
37641    }
37642
37643    // private
37644    function onShow(m){
37645        var last = active.last();
37646        lastShow = new Date();
37647        active.add(m);
37648        if(!attached){
37649            Roo.get(document).on("mousedown", onMouseDown);
37650            attached = true;
37651        }
37652        if(m.parentMenu){
37653           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37654           m.parentMenu.activeChild = m;
37655        }else if(last && last.isVisible()){
37656           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37657        }
37658    }
37659
37660    // private
37661    function onBeforeHide(m){
37662        if(m.activeChild){
37663            m.activeChild.hide();
37664        }
37665        if(m.autoHideTimer){
37666            clearTimeout(m.autoHideTimer);
37667            delete m.autoHideTimer;
37668        }
37669    }
37670
37671    // private
37672    function onBeforeShow(m){
37673        var pm = m.parentMenu;
37674        if(!pm && !m.allowOtherMenus){
37675            hideAll();
37676        }else if(pm && pm.activeChild && active != m){
37677            pm.activeChild.hide();
37678        }
37679    }
37680
37681    // private
37682    function onMouseDown(e){
37683        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37684            hideAll();
37685        }
37686    }
37687
37688    // private
37689    function onBeforeCheck(mi, state){
37690        if(state){
37691            var g = groups[mi.group];
37692            for(var i = 0, l = g.length; i < l; i++){
37693                if(g[i] != mi){
37694                    g[i].setChecked(false);
37695                }
37696            }
37697        }
37698    }
37699
37700    return {
37701
37702        /**
37703         * Hides all menus that are currently visible
37704         */
37705        hideAll : function(){
37706             hideAll();  
37707        },
37708
37709        // private
37710        register : function(menu){
37711            if(!menus){
37712                init();
37713            }
37714            menus[menu.id] = menu;
37715            menu.on("beforehide", onBeforeHide);
37716            menu.on("hide", onHide);
37717            menu.on("beforeshow", onBeforeShow);
37718            menu.on("show", onShow);
37719            var g = menu.group;
37720            if(g && menu.events["checkchange"]){
37721                if(!groups[g]){
37722                    groups[g] = [];
37723                }
37724                groups[g].push(menu);
37725                menu.on("checkchange", onCheck);
37726            }
37727        },
37728
37729         /**
37730          * Returns a {@link Roo.menu.Menu} object
37731          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37732          * be used to generate and return a new Menu instance.
37733          */
37734        get : function(menu){
37735            if(typeof menu == "string"){ // menu id
37736                return menus[menu];
37737            }else if(menu.events){  // menu instance
37738                return menu;
37739            }else if(typeof menu.length == 'number'){ // array of menu items?
37740                return new Roo.menu.Menu({items:menu});
37741            }else{ // otherwise, must be a config
37742                return new Roo.menu.Menu(menu);
37743            }
37744        },
37745
37746        // private
37747        unregister : function(menu){
37748            delete menus[menu.id];
37749            menu.un("beforehide", onBeforeHide);
37750            menu.un("hide", onHide);
37751            menu.un("beforeshow", onBeforeShow);
37752            menu.un("show", onShow);
37753            var g = menu.group;
37754            if(g && menu.events["checkchange"]){
37755                groups[g].remove(menu);
37756                menu.un("checkchange", onCheck);
37757            }
37758        },
37759
37760        // private
37761        registerCheckable : function(menuItem){
37762            var g = menuItem.group;
37763            if(g){
37764                if(!groups[g]){
37765                    groups[g] = [];
37766                }
37767                groups[g].push(menuItem);
37768                menuItem.on("beforecheckchange", onBeforeCheck);
37769            }
37770        },
37771
37772        // private
37773        unregisterCheckable : function(menuItem){
37774            var g = menuItem.group;
37775            if(g){
37776                groups[g].remove(menuItem);
37777                menuItem.un("beforecheckchange", onBeforeCheck);
37778            }
37779        }
37780    };
37781 }();/*
37782  * Based on:
37783  * Ext JS Library 1.1.1
37784  * Copyright(c) 2006-2007, Ext JS, LLC.
37785  *
37786  * Originally Released Under LGPL - original licence link has changed is not relivant.
37787  *
37788  * Fork - LGPL
37789  * <script type="text/javascript">
37790  */
37791  
37792
37793 /**
37794  * @class Roo.menu.BaseItem
37795  * @extends Roo.Component
37796  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37797  * management and base configuration options shared by all menu components.
37798  * @constructor
37799  * Creates a new BaseItem
37800  * @param {Object} config Configuration options
37801  */
37802 Roo.menu.BaseItem = function(config){
37803     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37804
37805     this.addEvents({
37806         /**
37807          * @event click
37808          * Fires when this item is clicked
37809          * @param {Roo.menu.BaseItem} this
37810          * @param {Roo.EventObject} e
37811          */
37812         click: true,
37813         /**
37814          * @event activate
37815          * Fires when this item is activated
37816          * @param {Roo.menu.BaseItem} this
37817          */
37818         activate : true,
37819         /**
37820          * @event deactivate
37821          * Fires when this item is deactivated
37822          * @param {Roo.menu.BaseItem} this
37823          */
37824         deactivate : true
37825     });
37826
37827     if(this.handler){
37828         this.on("click", this.handler, this.scope, true);
37829     }
37830 };
37831
37832 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37833     /**
37834      * @cfg {Function} handler
37835      * A function that will handle the click event of this menu item (defaults to undefined)
37836      */
37837     /**
37838      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37839      */
37840     canActivate : false,
37841     
37842      /**
37843      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37844      */
37845     hidden: false,
37846     
37847     /**
37848      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37849      */
37850     activeClass : "x-menu-item-active",
37851     /**
37852      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37853      */
37854     hideOnClick : true,
37855     /**
37856      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37857      */
37858     hideDelay : 100,
37859
37860     // private
37861     ctype: "Roo.menu.BaseItem",
37862
37863     // private
37864     actionMode : "container",
37865
37866     // private
37867     render : function(container, parentMenu){
37868         this.parentMenu = parentMenu;
37869         Roo.menu.BaseItem.superclass.render.call(this, container);
37870         this.container.menuItemId = this.id;
37871     },
37872
37873     // private
37874     onRender : function(container, position){
37875         this.el = Roo.get(this.el);
37876         container.dom.appendChild(this.el.dom);
37877     },
37878
37879     // private
37880     onClick : function(e){
37881         if(!this.disabled && this.fireEvent("click", this, e) !== false
37882                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37883             this.handleClick(e);
37884         }else{
37885             e.stopEvent();
37886         }
37887     },
37888
37889     // private
37890     activate : function(){
37891         if(this.disabled){
37892             return false;
37893         }
37894         var li = this.container;
37895         li.addClass(this.activeClass);
37896         this.region = li.getRegion().adjust(2, 2, -2, -2);
37897         this.fireEvent("activate", this);
37898         return true;
37899     },
37900
37901     // private
37902     deactivate : function(){
37903         this.container.removeClass(this.activeClass);
37904         this.fireEvent("deactivate", this);
37905     },
37906
37907     // private
37908     shouldDeactivate : function(e){
37909         return !this.region || !this.region.contains(e.getPoint());
37910     },
37911
37912     // private
37913     handleClick : function(e){
37914         if(this.hideOnClick){
37915             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37916         }
37917     },
37918
37919     // private
37920     expandMenu : function(autoActivate){
37921         // do nothing
37922     },
37923
37924     // private
37925     hideMenu : function(){
37926         // do nothing
37927     }
37928 });/*
37929  * Based on:
37930  * Ext JS Library 1.1.1
37931  * Copyright(c) 2006-2007, Ext JS, LLC.
37932  *
37933  * Originally Released Under LGPL - original licence link has changed is not relivant.
37934  *
37935  * Fork - LGPL
37936  * <script type="text/javascript">
37937  */
37938  
37939 /**
37940  * @class Roo.menu.Adapter
37941  * @extends Roo.menu.BaseItem
37942  * 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.
37943  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37944  * @constructor
37945  * Creates a new Adapter
37946  * @param {Object} config Configuration options
37947  */
37948 Roo.menu.Adapter = function(component, config){
37949     Roo.menu.Adapter.superclass.constructor.call(this, config);
37950     this.component = component;
37951 };
37952 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37953     // private
37954     canActivate : true,
37955
37956     // private
37957     onRender : function(container, position){
37958         this.component.render(container);
37959         this.el = this.component.getEl();
37960     },
37961
37962     // private
37963     activate : function(){
37964         if(this.disabled){
37965             return false;
37966         }
37967         this.component.focus();
37968         this.fireEvent("activate", this);
37969         return true;
37970     },
37971
37972     // private
37973     deactivate : function(){
37974         this.fireEvent("deactivate", this);
37975     },
37976
37977     // private
37978     disable : function(){
37979         this.component.disable();
37980         Roo.menu.Adapter.superclass.disable.call(this);
37981     },
37982
37983     // private
37984     enable : function(){
37985         this.component.enable();
37986         Roo.menu.Adapter.superclass.enable.call(this);
37987     }
37988 });/*
37989  * Based on:
37990  * Ext JS Library 1.1.1
37991  * Copyright(c) 2006-2007, Ext JS, LLC.
37992  *
37993  * Originally Released Under LGPL - original licence link has changed is not relivant.
37994  *
37995  * Fork - LGPL
37996  * <script type="text/javascript">
37997  */
37998
37999 /**
38000  * @class Roo.menu.TextItem
38001  * @extends Roo.menu.BaseItem
38002  * Adds a static text string to a menu, usually used as either a heading or group separator.
38003  * Note: old style constructor with text is still supported.
38004  * 
38005  * @constructor
38006  * Creates a new TextItem
38007  * @param {Object} cfg Configuration
38008  */
38009 Roo.menu.TextItem = function(cfg){
38010     if (typeof(cfg) == 'string') {
38011         this.text = cfg;
38012     } else {
38013         Roo.apply(this,cfg);
38014     }
38015     
38016     Roo.menu.TextItem.superclass.constructor.call(this);
38017 };
38018
38019 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38020     /**
38021      * @cfg {Boolean} text Text to show on item.
38022      */
38023     text : '',
38024     
38025     /**
38026      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38027      */
38028     hideOnClick : false,
38029     /**
38030      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38031      */
38032     itemCls : "x-menu-text",
38033
38034     // private
38035     onRender : function(){
38036         var s = document.createElement("span");
38037         s.className = this.itemCls;
38038         s.innerHTML = this.text;
38039         this.el = s;
38040         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38041     }
38042 });/*
38043  * Based on:
38044  * Ext JS Library 1.1.1
38045  * Copyright(c) 2006-2007, Ext JS, LLC.
38046  *
38047  * Originally Released Under LGPL - original licence link has changed is not relivant.
38048  *
38049  * Fork - LGPL
38050  * <script type="text/javascript">
38051  */
38052
38053 /**
38054  * @class Roo.menu.Separator
38055  * @extends Roo.menu.BaseItem
38056  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38057  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38058  * @constructor
38059  * @param {Object} config Configuration options
38060  */
38061 Roo.menu.Separator = function(config){
38062     Roo.menu.Separator.superclass.constructor.call(this, config);
38063 };
38064
38065 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38066     /**
38067      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38068      */
38069     itemCls : "x-menu-sep",
38070     /**
38071      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38072      */
38073     hideOnClick : false,
38074
38075     // private
38076     onRender : function(li){
38077         var s = document.createElement("span");
38078         s.className = this.itemCls;
38079         s.innerHTML = "&#160;";
38080         this.el = s;
38081         li.addClass("x-menu-sep-li");
38082         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38083     }
38084 });/*
38085  * Based on:
38086  * Ext JS Library 1.1.1
38087  * Copyright(c) 2006-2007, Ext JS, LLC.
38088  *
38089  * Originally Released Under LGPL - original licence link has changed is not relivant.
38090  *
38091  * Fork - LGPL
38092  * <script type="text/javascript">
38093  */
38094 /**
38095  * @class Roo.menu.Item
38096  * @extends Roo.menu.BaseItem
38097  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38098  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38099  * activation and click handling.
38100  * @constructor
38101  * Creates a new Item
38102  * @param {Object} config Configuration options
38103  */
38104 Roo.menu.Item = function(config){
38105     Roo.menu.Item.superclass.constructor.call(this, config);
38106     if(this.menu){
38107         this.menu = Roo.menu.MenuMgr.get(this.menu);
38108     }
38109 };
38110 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38111     
38112     /**
38113      * @cfg {String} text
38114      * The text to show on the menu item.
38115      */
38116     text: '',
38117      /**
38118      * @cfg {String} HTML to render in menu
38119      * The text to show on the menu item (HTML version).
38120      */
38121     html: '',
38122     /**
38123      * @cfg {String} icon
38124      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38125      */
38126     icon: undefined,
38127     /**
38128      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38129      */
38130     itemCls : "x-menu-item",
38131     /**
38132      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38133      */
38134     canActivate : true,
38135     /**
38136      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38137      */
38138     showDelay: 200,
38139     // doc'd in BaseItem
38140     hideDelay: 200,
38141
38142     // private
38143     ctype: "Roo.menu.Item",
38144     
38145     // private
38146     onRender : function(container, position){
38147         var el = document.createElement("a");
38148         el.hideFocus = true;
38149         el.unselectable = "on";
38150         el.href = this.href || "#";
38151         if(this.hrefTarget){
38152             el.target = this.hrefTarget;
38153         }
38154         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38155         
38156         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38157         
38158         el.innerHTML = String.format(
38159                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38160                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38161         this.el = el;
38162         Roo.menu.Item.superclass.onRender.call(this, container, position);
38163     },
38164
38165     /**
38166      * Sets the text to display in this menu item
38167      * @param {String} text The text to display
38168      * @param {Boolean} isHTML true to indicate text is pure html.
38169      */
38170     setText : function(text, isHTML){
38171         if (isHTML) {
38172             this.html = text;
38173         } else {
38174             this.text = text;
38175             this.html = '';
38176         }
38177         if(this.rendered){
38178             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38179      
38180             this.el.update(String.format(
38181                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38182                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38183             this.parentMenu.autoWidth();
38184         }
38185     },
38186
38187     // private
38188     handleClick : function(e){
38189         if(!this.href){ // if no link defined, stop the event automatically
38190             e.stopEvent();
38191         }
38192         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38193     },
38194
38195     // private
38196     activate : function(autoExpand){
38197         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38198             this.focus();
38199             if(autoExpand){
38200                 this.expandMenu();
38201             }
38202         }
38203         return true;
38204     },
38205
38206     // private
38207     shouldDeactivate : function(e){
38208         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38209             if(this.menu && this.menu.isVisible()){
38210                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38211             }
38212             return true;
38213         }
38214         return false;
38215     },
38216
38217     // private
38218     deactivate : function(){
38219         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38220         this.hideMenu();
38221     },
38222
38223     // private
38224     expandMenu : function(autoActivate){
38225         if(!this.disabled && this.menu){
38226             clearTimeout(this.hideTimer);
38227             delete this.hideTimer;
38228             if(!this.menu.isVisible() && !this.showTimer){
38229                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38230             }else if (this.menu.isVisible() && autoActivate){
38231                 this.menu.tryActivate(0, 1);
38232             }
38233         }
38234     },
38235
38236     // private
38237     deferExpand : function(autoActivate){
38238         delete this.showTimer;
38239         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38240         if(autoActivate){
38241             this.menu.tryActivate(0, 1);
38242         }
38243     },
38244
38245     // private
38246     hideMenu : function(){
38247         clearTimeout(this.showTimer);
38248         delete this.showTimer;
38249         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38250             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38251         }
38252     },
38253
38254     // private
38255     deferHide : function(){
38256         delete this.hideTimer;
38257         this.menu.hide();
38258     }
38259 });/*
38260  * Based on:
38261  * Ext JS Library 1.1.1
38262  * Copyright(c) 2006-2007, Ext JS, LLC.
38263  *
38264  * Originally Released Under LGPL - original licence link has changed is not relivant.
38265  *
38266  * Fork - LGPL
38267  * <script type="text/javascript">
38268  */
38269  
38270 /**
38271  * @class Roo.menu.CheckItem
38272  * @extends Roo.menu.Item
38273  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38274  * @constructor
38275  * Creates a new CheckItem
38276  * @param {Object} config Configuration options
38277  */
38278 Roo.menu.CheckItem = function(config){
38279     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38280     this.addEvents({
38281         /**
38282          * @event beforecheckchange
38283          * Fires before the checked value is set, providing an opportunity to cancel if needed
38284          * @param {Roo.menu.CheckItem} this
38285          * @param {Boolean} checked The new checked value that will be set
38286          */
38287         "beforecheckchange" : true,
38288         /**
38289          * @event checkchange
38290          * Fires after the checked value has been set
38291          * @param {Roo.menu.CheckItem} this
38292          * @param {Boolean} checked The checked value that was set
38293          */
38294         "checkchange" : true
38295     });
38296     if(this.checkHandler){
38297         this.on('checkchange', this.checkHandler, this.scope);
38298     }
38299 };
38300 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38301     /**
38302      * @cfg {String} group
38303      * All check items with the same group name will automatically be grouped into a single-select
38304      * radio button group (defaults to '')
38305      */
38306     /**
38307      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38308      */
38309     itemCls : "x-menu-item x-menu-check-item",
38310     /**
38311      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38312      */
38313     groupClass : "x-menu-group-item",
38314
38315     /**
38316      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38317      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38318      * initialized with checked = true will be rendered as checked.
38319      */
38320     checked: false,
38321
38322     // private
38323     ctype: "Roo.menu.CheckItem",
38324
38325     // private
38326     onRender : function(c){
38327         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38328         if(this.group){
38329             this.el.addClass(this.groupClass);
38330         }
38331         Roo.menu.MenuMgr.registerCheckable(this);
38332         if(this.checked){
38333             this.checked = false;
38334             this.setChecked(true, true);
38335         }
38336     },
38337
38338     // private
38339     destroy : function(){
38340         if(this.rendered){
38341             Roo.menu.MenuMgr.unregisterCheckable(this);
38342         }
38343         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38344     },
38345
38346     /**
38347      * Set the checked state of this item
38348      * @param {Boolean} checked The new checked value
38349      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38350      */
38351     setChecked : function(state, suppressEvent){
38352         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38353             if(this.container){
38354                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38355             }
38356             this.checked = state;
38357             if(suppressEvent !== true){
38358                 this.fireEvent("checkchange", this, state);
38359             }
38360         }
38361     },
38362
38363     // private
38364     handleClick : function(e){
38365        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38366            this.setChecked(!this.checked);
38367        }
38368        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38369     }
38370 });/*
38371  * Based on:
38372  * Ext JS Library 1.1.1
38373  * Copyright(c) 2006-2007, Ext JS, LLC.
38374  *
38375  * Originally Released Under LGPL - original licence link has changed is not relivant.
38376  *
38377  * Fork - LGPL
38378  * <script type="text/javascript">
38379  */
38380  
38381 /**
38382  * @class Roo.menu.DateItem
38383  * @extends Roo.menu.Adapter
38384  * A menu item that wraps the {@link Roo.DatPicker} component.
38385  * @constructor
38386  * Creates a new DateItem
38387  * @param {Object} config Configuration options
38388  */
38389 Roo.menu.DateItem = function(config){
38390     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38391     /** The Roo.DatePicker object @type Roo.DatePicker */
38392     this.picker = this.component;
38393     this.addEvents({select: true});
38394     
38395     this.picker.on("render", function(picker){
38396         picker.getEl().swallowEvent("click");
38397         picker.container.addClass("x-menu-date-item");
38398     });
38399
38400     this.picker.on("select", this.onSelect, this);
38401 };
38402
38403 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38404     // private
38405     onSelect : function(picker, date){
38406         this.fireEvent("select", this, date, picker);
38407         Roo.menu.DateItem.superclass.handleClick.call(this);
38408     }
38409 });/*
38410  * Based on:
38411  * Ext JS Library 1.1.1
38412  * Copyright(c) 2006-2007, Ext JS, LLC.
38413  *
38414  * Originally Released Under LGPL - original licence link has changed is not relivant.
38415  *
38416  * Fork - LGPL
38417  * <script type="text/javascript">
38418  */
38419  
38420 /**
38421  * @class Roo.menu.ColorItem
38422  * @extends Roo.menu.Adapter
38423  * A menu item that wraps the {@link Roo.ColorPalette} component.
38424  * @constructor
38425  * Creates a new ColorItem
38426  * @param {Object} config Configuration options
38427  */
38428 Roo.menu.ColorItem = function(config){
38429     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38430     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38431     this.palette = this.component;
38432     this.relayEvents(this.palette, ["select"]);
38433     if(this.selectHandler){
38434         this.on('select', this.selectHandler, this.scope);
38435     }
38436 };
38437 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38438  * Based on:
38439  * Ext JS Library 1.1.1
38440  * Copyright(c) 2006-2007, Ext JS, LLC.
38441  *
38442  * Originally Released Under LGPL - original licence link has changed is not relivant.
38443  *
38444  * Fork - LGPL
38445  * <script type="text/javascript">
38446  */
38447  
38448
38449 /**
38450  * @class Roo.menu.DateMenu
38451  * @extends Roo.menu.Menu
38452  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38453  * @constructor
38454  * Creates a new DateMenu
38455  * @param {Object} config Configuration options
38456  */
38457 Roo.menu.DateMenu = function(config){
38458     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38459     this.plain = true;
38460     var di = new Roo.menu.DateItem(config);
38461     this.add(di);
38462     /**
38463      * The {@link Roo.DatePicker} instance for this DateMenu
38464      * @type DatePicker
38465      */
38466     this.picker = di.picker;
38467     /**
38468      * @event select
38469      * @param {DatePicker} picker
38470      * @param {Date} date
38471      */
38472     this.relayEvents(di, ["select"]);
38473     this.on('beforeshow', function(){
38474         if(this.picker){
38475             this.picker.hideMonthPicker(false);
38476         }
38477     }, this);
38478 };
38479 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38480     cls:'x-date-menu'
38481 });/*
38482  * Based on:
38483  * Ext JS Library 1.1.1
38484  * Copyright(c) 2006-2007, Ext JS, LLC.
38485  *
38486  * Originally Released Under LGPL - original licence link has changed is not relivant.
38487  *
38488  * Fork - LGPL
38489  * <script type="text/javascript">
38490  */
38491  
38492
38493 /**
38494  * @class Roo.menu.ColorMenu
38495  * @extends Roo.menu.Menu
38496  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38497  * @constructor
38498  * Creates a new ColorMenu
38499  * @param {Object} config Configuration options
38500  */
38501 Roo.menu.ColorMenu = function(config){
38502     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38503     this.plain = true;
38504     var ci = new Roo.menu.ColorItem(config);
38505     this.add(ci);
38506     /**
38507      * The {@link Roo.ColorPalette} instance for this ColorMenu
38508      * @type ColorPalette
38509      */
38510     this.palette = ci.palette;
38511     /**
38512      * @event select
38513      * @param {ColorPalette} palette
38514      * @param {String} color
38515      */
38516     this.relayEvents(ci, ["select"]);
38517 };
38518 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38519  * Based on:
38520  * Ext JS Library 1.1.1
38521  * Copyright(c) 2006-2007, Ext JS, LLC.
38522  *
38523  * Originally Released Under LGPL - original licence link has changed is not relivant.
38524  *
38525  * Fork - LGPL
38526  * <script type="text/javascript">
38527  */
38528  
38529 /**
38530  * @class Roo.form.Field
38531  * @extends Roo.BoxComponent
38532  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38533  * @constructor
38534  * Creates a new Field
38535  * @param {Object} config Configuration options
38536  */
38537 Roo.form.Field = function(config){
38538     Roo.form.Field.superclass.constructor.call(this, config);
38539 };
38540
38541 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38542     /**
38543      * @cfg {String} fieldLabel Label to use when rendering a form.
38544      */
38545        /**
38546      * @cfg {String} qtip Mouse over tip
38547      */
38548      
38549     /**
38550      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38551      */
38552     invalidClass : "x-form-invalid",
38553     /**
38554      * @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")
38555      */
38556     invalidText : "The value in this field is invalid",
38557     /**
38558      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38559      */
38560     focusClass : "x-form-focus",
38561     /**
38562      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38563       automatic validation (defaults to "keyup").
38564      */
38565     validationEvent : "keyup",
38566     /**
38567      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38568      */
38569     validateOnBlur : true,
38570     /**
38571      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38572      */
38573     validationDelay : 250,
38574     /**
38575      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38576      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38577      */
38578     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38579     /**
38580      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38581      */
38582     fieldClass : "x-form-field",
38583     /**
38584      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38585      *<pre>
38586 Value         Description
38587 -----------   ----------------------------------------------------------------------
38588 qtip          Display a quick tip when the user hovers over the field
38589 title         Display a default browser title attribute popup
38590 under         Add a block div beneath the field containing the error text
38591 side          Add an error icon to the right of the field with a popup on hover
38592 [element id]  Add the error text directly to the innerHTML of the specified element
38593 </pre>
38594      */
38595     msgTarget : 'qtip',
38596     /**
38597      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38598      */
38599     msgFx : 'normal',
38600
38601     /**
38602      * @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.
38603      */
38604     readOnly : false,
38605
38606     /**
38607      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38608      */
38609     disabled : false,
38610
38611     /**
38612      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38613      */
38614     inputType : undefined,
38615     
38616     /**
38617      * @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).
38618          */
38619         tabIndex : undefined,
38620         
38621     // private
38622     isFormField : true,
38623
38624     // private
38625     hasFocus : false,
38626     /**
38627      * @property {Roo.Element} fieldEl
38628      * Element Containing the rendered Field (with label etc.)
38629      */
38630     /**
38631      * @cfg {Mixed} value A value to initialize this field with.
38632      */
38633     value : undefined,
38634
38635     /**
38636      * @cfg {String} name The field's HTML name attribute.
38637      */
38638     /**
38639      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38640      */
38641     // private
38642     loadedValue : false,
38643      
38644      
38645         // private ??
38646         initComponent : function(){
38647         Roo.form.Field.superclass.initComponent.call(this);
38648         this.addEvents({
38649             /**
38650              * @event focus
38651              * Fires when this field receives input focus.
38652              * @param {Roo.form.Field} this
38653              */
38654             focus : true,
38655             /**
38656              * @event blur
38657              * Fires when this field loses input focus.
38658              * @param {Roo.form.Field} this
38659              */
38660             blur : true,
38661             /**
38662              * @event specialkey
38663              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38664              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38665              * @param {Roo.form.Field} this
38666              * @param {Roo.EventObject} e The event object
38667              */
38668             specialkey : true,
38669             /**
38670              * @event change
38671              * Fires just before the field blurs if the field value has changed.
38672              * @param {Roo.form.Field} this
38673              * @param {Mixed} newValue The new value
38674              * @param {Mixed} oldValue The original value
38675              */
38676             change : true,
38677             /**
38678              * @event invalid
38679              * Fires after the field has been marked as invalid.
38680              * @param {Roo.form.Field} this
38681              * @param {String} msg The validation message
38682              */
38683             invalid : true,
38684             /**
38685              * @event valid
38686              * Fires after the field has been validated with no errors.
38687              * @param {Roo.form.Field} this
38688              */
38689             valid : true,
38690              /**
38691              * @event keyup
38692              * Fires after the key up
38693              * @param {Roo.form.Field} this
38694              * @param {Roo.EventObject}  e The event Object
38695              */
38696             keyup : true
38697         });
38698     },
38699
38700     /**
38701      * Returns the name attribute of the field if available
38702      * @return {String} name The field name
38703      */
38704     getName: function(){
38705          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38706     },
38707
38708     // private
38709     onRender : function(ct, position){
38710         Roo.form.Field.superclass.onRender.call(this, ct, position);
38711         if(!this.el){
38712             var cfg = this.getAutoCreate();
38713             if(!cfg.name){
38714                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38715             }
38716             if (!cfg.name.length) {
38717                 delete cfg.name;
38718             }
38719             if(this.inputType){
38720                 cfg.type = this.inputType;
38721             }
38722             this.el = ct.createChild(cfg, position);
38723         }
38724         var type = this.el.dom.type;
38725         if(type){
38726             if(type == 'password'){
38727                 type = 'text';
38728             }
38729             this.el.addClass('x-form-'+type);
38730         }
38731         if(this.readOnly){
38732             this.el.dom.readOnly = true;
38733         }
38734         if(this.tabIndex !== undefined){
38735             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38736         }
38737
38738         this.el.addClass([this.fieldClass, this.cls]);
38739         this.initValue();
38740     },
38741
38742     /**
38743      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38744      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38745      * @return {Roo.form.Field} this
38746      */
38747     applyTo : function(target){
38748         this.allowDomMove = false;
38749         this.el = Roo.get(target);
38750         this.render(this.el.dom.parentNode);
38751         return this;
38752     },
38753
38754     // private
38755     initValue : function(){
38756         if(this.value !== undefined){
38757             this.setValue(this.value);
38758         }else if(this.el.dom.value.length > 0){
38759             this.setValue(this.el.dom.value);
38760         }
38761     },
38762
38763     /**
38764      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38765      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38766      */
38767     isDirty : function() {
38768         if(this.disabled) {
38769             return false;
38770         }
38771         return String(this.getValue()) !== String(this.originalValue);
38772     },
38773
38774     /**
38775      * stores the current value in loadedValue
38776      */
38777     resetHasChanged : function()
38778     {
38779         this.loadedValue = String(this.getValue());
38780     },
38781     /**
38782      * checks the current value against the 'loaded' value.
38783      * Note - will return false if 'resetHasChanged' has not been called first.
38784      */
38785     hasChanged : function()
38786     {
38787         if(this.disabled || this.readOnly) {
38788             return false;
38789         }
38790         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38791     },
38792     
38793     
38794     
38795     // private
38796     afterRender : function(){
38797         Roo.form.Field.superclass.afterRender.call(this);
38798         this.initEvents();
38799     },
38800
38801     // private
38802     fireKey : function(e){
38803         //Roo.log('field ' + e.getKey());
38804         if(e.isNavKeyPress()){
38805             this.fireEvent("specialkey", this, e);
38806         }
38807     },
38808
38809     /**
38810      * Resets the current field value to the originally loaded value and clears any validation messages
38811      */
38812     reset : function(){
38813         this.setValue(this.resetValue);
38814         this.clearInvalid();
38815     },
38816
38817     // private
38818     initEvents : function(){
38819         // safari killled keypress - so keydown is now used..
38820         this.el.on("keydown" , this.fireKey,  this);
38821         this.el.on("focus", this.onFocus,  this);
38822         this.el.on("blur", this.onBlur,  this);
38823         this.el.relayEvent('keyup', this);
38824
38825         // reference to original value for reset
38826         this.originalValue = this.getValue();
38827         this.resetValue =  this.getValue();
38828     },
38829
38830     // private
38831     onFocus : function(){
38832         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38833             this.el.addClass(this.focusClass);
38834         }
38835         if(!this.hasFocus){
38836             this.hasFocus = true;
38837             this.startValue = this.getValue();
38838             this.fireEvent("focus", this);
38839         }
38840     },
38841
38842     beforeBlur : Roo.emptyFn,
38843
38844     // private
38845     onBlur : function(){
38846         this.beforeBlur();
38847         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38848             this.el.removeClass(this.focusClass);
38849         }
38850         this.hasFocus = false;
38851         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38852             this.validate();
38853         }
38854         var v = this.getValue();
38855         if(String(v) !== String(this.startValue)){
38856             this.fireEvent('change', this, v, this.startValue);
38857         }
38858         this.fireEvent("blur", this);
38859     },
38860
38861     /**
38862      * Returns whether or not the field value is currently valid
38863      * @param {Boolean} preventMark True to disable marking the field invalid
38864      * @return {Boolean} True if the value is valid, else false
38865      */
38866     isValid : function(preventMark){
38867         if(this.disabled){
38868             return true;
38869         }
38870         var restore = this.preventMark;
38871         this.preventMark = preventMark === true;
38872         var v = this.validateValue(this.processValue(this.getRawValue()));
38873         this.preventMark = restore;
38874         return v;
38875     },
38876
38877     /**
38878      * Validates the field value
38879      * @return {Boolean} True if the value is valid, else false
38880      */
38881     validate : function(){
38882         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38883             this.clearInvalid();
38884             return true;
38885         }
38886         return false;
38887     },
38888
38889     processValue : function(value){
38890         return value;
38891     },
38892
38893     // private
38894     // Subclasses should provide the validation implementation by overriding this
38895     validateValue : function(value){
38896         return true;
38897     },
38898
38899     /**
38900      * Mark this field as invalid
38901      * @param {String} msg The validation message
38902      */
38903     markInvalid : function(msg){
38904         if(!this.rendered || this.preventMark){ // not rendered
38905             return;
38906         }
38907         
38908         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38909         
38910         obj.el.addClass(this.invalidClass);
38911         msg = msg || this.invalidText;
38912         switch(this.msgTarget){
38913             case 'qtip':
38914                 obj.el.dom.qtip = msg;
38915                 obj.el.dom.qclass = 'x-form-invalid-tip';
38916                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38917                     Roo.QuickTips.enable();
38918                 }
38919                 break;
38920             case 'title':
38921                 this.el.dom.title = msg;
38922                 break;
38923             case 'under':
38924                 if(!this.errorEl){
38925                     var elp = this.el.findParent('.x-form-element', 5, true);
38926                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38927                     this.errorEl.setWidth(elp.getWidth(true)-20);
38928                 }
38929                 this.errorEl.update(msg);
38930                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38931                 break;
38932             case 'side':
38933                 if(!this.errorIcon){
38934                     var elp = this.el.findParent('.x-form-element', 5, true);
38935                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38936                 }
38937                 this.alignErrorIcon();
38938                 this.errorIcon.dom.qtip = msg;
38939                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38940                 this.errorIcon.show();
38941                 this.on('resize', this.alignErrorIcon, this);
38942                 break;
38943             default:
38944                 var t = Roo.getDom(this.msgTarget);
38945                 t.innerHTML = msg;
38946                 t.style.display = this.msgDisplay;
38947                 break;
38948         }
38949         this.fireEvent('invalid', this, msg);
38950     },
38951
38952     // private
38953     alignErrorIcon : function(){
38954         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
38955     },
38956
38957     /**
38958      * Clear any invalid styles/messages for this field
38959      */
38960     clearInvalid : function(){
38961         if(!this.rendered || this.preventMark){ // not rendered
38962             return;
38963         }
38964         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38965         
38966         obj.el.removeClass(this.invalidClass);
38967         switch(this.msgTarget){
38968             case 'qtip':
38969                 obj.el.dom.qtip = '';
38970                 break;
38971             case 'title':
38972                 this.el.dom.title = '';
38973                 break;
38974             case 'under':
38975                 if(this.errorEl){
38976                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
38977                 }
38978                 break;
38979             case 'side':
38980                 if(this.errorIcon){
38981                     this.errorIcon.dom.qtip = '';
38982                     this.errorIcon.hide();
38983                     this.un('resize', this.alignErrorIcon, this);
38984                 }
38985                 break;
38986             default:
38987                 var t = Roo.getDom(this.msgTarget);
38988                 t.innerHTML = '';
38989                 t.style.display = 'none';
38990                 break;
38991         }
38992         this.fireEvent('valid', this);
38993     },
38994
38995     /**
38996      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
38997      * @return {Mixed} value The field value
38998      */
38999     getRawValue : function(){
39000         var v = this.el.getValue();
39001         
39002         return v;
39003     },
39004
39005     /**
39006      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39007      * @return {Mixed} value The field value
39008      */
39009     getValue : function(){
39010         var v = this.el.getValue();
39011          
39012         return v;
39013     },
39014
39015     /**
39016      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39017      * @param {Mixed} value The value to set
39018      */
39019     setRawValue : function(v){
39020         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39021     },
39022
39023     /**
39024      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39025      * @param {Mixed} value The value to set
39026      */
39027     setValue : function(v){
39028         this.value = v;
39029         if(this.rendered){
39030             this.el.dom.value = (v === null || v === undefined ? '' : v);
39031              this.validate();
39032         }
39033     },
39034
39035     adjustSize : function(w, h){
39036         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39037         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39038         return s;
39039     },
39040
39041     adjustWidth : function(tag, w){
39042         tag = tag.toLowerCase();
39043         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39044             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39045                 if(tag == 'input'){
39046                     return w + 2;
39047                 }
39048                 if(tag == 'textarea'){
39049                     return w-2;
39050                 }
39051             }else if(Roo.isOpera){
39052                 if(tag == 'input'){
39053                     return w + 2;
39054                 }
39055                 if(tag == 'textarea'){
39056                     return w-2;
39057                 }
39058             }
39059         }
39060         return w;
39061     }
39062 });
39063
39064
39065 // anything other than normal should be considered experimental
39066 Roo.form.Field.msgFx = {
39067     normal : {
39068         show: function(msgEl, f){
39069             msgEl.setDisplayed('block');
39070         },
39071
39072         hide : function(msgEl, f){
39073             msgEl.setDisplayed(false).update('');
39074         }
39075     },
39076
39077     slide : {
39078         show: function(msgEl, f){
39079             msgEl.slideIn('t', {stopFx:true});
39080         },
39081
39082         hide : function(msgEl, f){
39083             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39084         }
39085     },
39086
39087     slideRight : {
39088         show: function(msgEl, f){
39089             msgEl.fixDisplay();
39090             msgEl.alignTo(f.el, 'tl-tr');
39091             msgEl.slideIn('l', {stopFx:true});
39092         },
39093
39094         hide : function(msgEl, f){
39095             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39096         }
39097     }
39098 };/*
39099  * Based on:
39100  * Ext JS Library 1.1.1
39101  * Copyright(c) 2006-2007, Ext JS, LLC.
39102  *
39103  * Originally Released Under LGPL - original licence link has changed is not relivant.
39104  *
39105  * Fork - LGPL
39106  * <script type="text/javascript">
39107  */
39108  
39109
39110 /**
39111  * @class Roo.form.TextField
39112  * @extends Roo.form.Field
39113  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39114  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39115  * @constructor
39116  * Creates a new TextField
39117  * @param {Object} config Configuration options
39118  */
39119 Roo.form.TextField = function(config){
39120     Roo.form.TextField.superclass.constructor.call(this, config);
39121     this.addEvents({
39122         /**
39123          * @event autosize
39124          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39125          * according to the default logic, but this event provides a hook for the developer to apply additional
39126          * logic at runtime to resize the field if needed.
39127              * @param {Roo.form.Field} this This text field
39128              * @param {Number} width The new field width
39129              */
39130         autosize : true
39131     });
39132 };
39133
39134 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39135     /**
39136      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39137      */
39138     grow : false,
39139     /**
39140      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39141      */
39142     growMin : 30,
39143     /**
39144      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39145      */
39146     growMax : 800,
39147     /**
39148      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39149      */
39150     vtype : null,
39151     /**
39152      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39153      */
39154     maskRe : null,
39155     /**
39156      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39157      */
39158     disableKeyFilter : false,
39159     /**
39160      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39161      */
39162     allowBlank : true,
39163     /**
39164      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39165      */
39166     minLength : 0,
39167     /**
39168      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39169      */
39170     maxLength : Number.MAX_VALUE,
39171     /**
39172      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39173      */
39174     minLengthText : "The minimum length for this field is {0}",
39175     /**
39176      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39177      */
39178     maxLengthText : "The maximum length for this field is {0}",
39179     /**
39180      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39181      */
39182     selectOnFocus : false,
39183     /**
39184      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39185      */
39186     blankText : "This field is required",
39187     /**
39188      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39189      * If available, this function will be called only after the basic validators all return true, and will be passed the
39190      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39191      */
39192     validator : null,
39193     /**
39194      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39195      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39196      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39197      */
39198     regex : null,
39199     /**
39200      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39201      */
39202     regexText : "",
39203     /**
39204      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39205      */
39206     emptyText : null,
39207    
39208
39209     // private
39210     initEvents : function()
39211     {
39212         if (this.emptyText) {
39213             this.el.attr('placeholder', this.emptyText);
39214         }
39215         
39216         Roo.form.TextField.superclass.initEvents.call(this);
39217         if(this.validationEvent == 'keyup'){
39218             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39219             this.el.on('keyup', this.filterValidation, this);
39220         }
39221         else if(this.validationEvent !== false){
39222             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39223         }
39224         
39225         if(this.selectOnFocus){
39226             this.on("focus", this.preFocus, this);
39227             
39228         }
39229         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39230             this.el.on("keypress", this.filterKeys, this);
39231         }
39232         if(this.grow){
39233             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39234             this.el.on("click", this.autoSize,  this);
39235         }
39236         if(this.el.is('input[type=password]') && Roo.isSafari){
39237             this.el.on('keydown', this.SafariOnKeyDown, this);
39238         }
39239     },
39240
39241     processValue : function(value){
39242         if(this.stripCharsRe){
39243             var newValue = value.replace(this.stripCharsRe, '');
39244             if(newValue !== value){
39245                 this.setRawValue(newValue);
39246                 return newValue;
39247             }
39248         }
39249         return value;
39250     },
39251
39252     filterValidation : function(e){
39253         if(!e.isNavKeyPress()){
39254             this.validationTask.delay(this.validationDelay);
39255         }
39256     },
39257
39258     // private
39259     onKeyUp : function(e){
39260         if(!e.isNavKeyPress()){
39261             this.autoSize();
39262         }
39263     },
39264
39265     /**
39266      * Resets the current field value to the originally-loaded value and clears any validation messages.
39267      *  
39268      */
39269     reset : function(){
39270         Roo.form.TextField.superclass.reset.call(this);
39271        
39272     },
39273
39274     
39275     // private
39276     preFocus : function(){
39277         
39278         if(this.selectOnFocus){
39279             this.el.dom.select();
39280         }
39281     },
39282
39283     
39284     // private
39285     filterKeys : function(e){
39286         var k = e.getKey();
39287         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39288             return;
39289         }
39290         var c = e.getCharCode(), cc = String.fromCharCode(c);
39291         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39292             return;
39293         }
39294         if(!this.maskRe.test(cc)){
39295             e.stopEvent();
39296         }
39297     },
39298
39299     setValue : function(v){
39300         
39301         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39302         
39303         this.autoSize();
39304     },
39305
39306     /**
39307      * Validates a value according to the field's validation rules and marks the field as invalid
39308      * if the validation fails
39309      * @param {Mixed} value The value to validate
39310      * @return {Boolean} True if the value is valid, else false
39311      */
39312     validateValue : function(value){
39313         if(value.length < 1)  { // if it's blank
39314              if(this.allowBlank){
39315                 this.clearInvalid();
39316                 return true;
39317              }else{
39318                 this.markInvalid(this.blankText);
39319                 return false;
39320              }
39321         }
39322         if(value.length < this.minLength){
39323             this.markInvalid(String.format(this.minLengthText, this.minLength));
39324             return false;
39325         }
39326         if(value.length > this.maxLength){
39327             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39328             return false;
39329         }
39330         if(this.vtype){
39331             var vt = Roo.form.VTypes;
39332             if(!vt[this.vtype](value, this)){
39333                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39334                 return false;
39335             }
39336         }
39337         if(typeof this.validator == "function"){
39338             var msg = this.validator(value);
39339             if(msg !== true){
39340                 this.markInvalid(msg);
39341                 return false;
39342             }
39343         }
39344         if(this.regex && !this.regex.test(value)){
39345             this.markInvalid(this.regexText);
39346             return false;
39347         }
39348         return true;
39349     },
39350
39351     /**
39352      * Selects text in this field
39353      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39354      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39355      */
39356     selectText : function(start, end){
39357         var v = this.getRawValue();
39358         if(v.length > 0){
39359             start = start === undefined ? 0 : start;
39360             end = end === undefined ? v.length : end;
39361             var d = this.el.dom;
39362             if(d.setSelectionRange){
39363                 d.setSelectionRange(start, end);
39364             }else if(d.createTextRange){
39365                 var range = d.createTextRange();
39366                 range.moveStart("character", start);
39367                 range.moveEnd("character", v.length-end);
39368                 range.select();
39369             }
39370         }
39371     },
39372
39373     /**
39374      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39375      * This only takes effect if grow = true, and fires the autosize event.
39376      */
39377     autoSize : function(){
39378         if(!this.grow || !this.rendered){
39379             return;
39380         }
39381         if(!this.metrics){
39382             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39383         }
39384         var el = this.el;
39385         var v = el.dom.value;
39386         var d = document.createElement('div');
39387         d.appendChild(document.createTextNode(v));
39388         v = d.innerHTML;
39389         d = null;
39390         v += "&#160;";
39391         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39392         this.el.setWidth(w);
39393         this.fireEvent("autosize", this, w);
39394     },
39395     
39396     // private
39397     SafariOnKeyDown : function(event)
39398     {
39399         // this is a workaround for a password hang bug on chrome/ webkit.
39400         
39401         var isSelectAll = false;
39402         
39403         if(this.el.dom.selectionEnd > 0){
39404             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39405         }
39406         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39407             event.preventDefault();
39408             this.setValue('');
39409             return;
39410         }
39411         
39412         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39413             
39414             event.preventDefault();
39415             // this is very hacky as keydown always get's upper case.
39416             
39417             var cc = String.fromCharCode(event.getCharCode());
39418             
39419             
39420             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39421             
39422         }
39423         
39424         
39425     }
39426 });/*
39427  * Based on:
39428  * Ext JS Library 1.1.1
39429  * Copyright(c) 2006-2007, Ext JS, LLC.
39430  *
39431  * Originally Released Under LGPL - original licence link has changed is not relivant.
39432  *
39433  * Fork - LGPL
39434  * <script type="text/javascript">
39435  */
39436  
39437 /**
39438  * @class Roo.form.Hidden
39439  * @extends Roo.form.TextField
39440  * Simple Hidden element used on forms 
39441  * 
39442  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39443  * 
39444  * @constructor
39445  * Creates a new Hidden form element.
39446  * @param {Object} config Configuration options
39447  */
39448
39449
39450
39451 // easy hidden field...
39452 Roo.form.Hidden = function(config){
39453     Roo.form.Hidden.superclass.constructor.call(this, config);
39454 };
39455   
39456 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39457     fieldLabel:      '',
39458     inputType:      'hidden',
39459     width:          50,
39460     allowBlank:     true,
39461     labelSeparator: '',
39462     hidden:         true,
39463     itemCls :       'x-form-item-display-none'
39464
39465
39466 });
39467
39468
39469 /*
39470  * Based on:
39471  * Ext JS Library 1.1.1
39472  * Copyright(c) 2006-2007, Ext JS, LLC.
39473  *
39474  * Originally Released Under LGPL - original licence link has changed is not relivant.
39475  *
39476  * Fork - LGPL
39477  * <script type="text/javascript">
39478  */
39479  
39480 /**
39481  * @class Roo.form.TriggerField
39482  * @extends Roo.form.TextField
39483  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39484  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39485  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39486  * for which you can provide a custom implementation.  For example:
39487  * <pre><code>
39488 var trigger = new Roo.form.TriggerField();
39489 trigger.onTriggerClick = myTriggerFn;
39490 trigger.applyTo('my-field');
39491 </code></pre>
39492  *
39493  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39494  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39495  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39496  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39497  * @constructor
39498  * Create a new TriggerField.
39499  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39500  * to the base TextField)
39501  */
39502 Roo.form.TriggerField = function(config){
39503     this.mimicing = false;
39504     Roo.form.TriggerField.superclass.constructor.call(this, config);
39505 };
39506
39507 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39508     /**
39509      * @cfg {String} triggerClass A CSS class to apply to the trigger
39510      */
39511     /**
39512      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39513      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39514      */
39515     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39516     /**
39517      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39518      */
39519     hideTrigger:false,
39520
39521     /** @cfg {Boolean} grow @hide */
39522     /** @cfg {Number} growMin @hide */
39523     /** @cfg {Number} growMax @hide */
39524
39525     /**
39526      * @hide 
39527      * @method
39528      */
39529     autoSize: Roo.emptyFn,
39530     // private
39531     monitorTab : true,
39532     // private
39533     deferHeight : true,
39534
39535     
39536     actionMode : 'wrap',
39537     // private
39538     onResize : function(w, h){
39539         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39540         if(typeof w == 'number'){
39541             var x = w - this.trigger.getWidth();
39542             this.el.setWidth(this.adjustWidth('input', x));
39543             this.trigger.setStyle('left', x+'px');
39544         }
39545     },
39546
39547     // private
39548     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39549
39550     // private
39551     getResizeEl : function(){
39552         return this.wrap;
39553     },
39554
39555     // private
39556     getPositionEl : function(){
39557         return this.wrap;
39558     },
39559
39560     // private
39561     alignErrorIcon : function(){
39562         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39563     },
39564
39565     // private
39566     onRender : function(ct, position){
39567         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39568         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39569         this.trigger = this.wrap.createChild(this.triggerConfig ||
39570                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39571         if(this.hideTrigger){
39572             this.trigger.setDisplayed(false);
39573         }
39574         this.initTrigger();
39575         if(!this.width){
39576             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39577         }
39578     },
39579
39580     // private
39581     initTrigger : function(){
39582         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39583         this.trigger.addClassOnOver('x-form-trigger-over');
39584         this.trigger.addClassOnClick('x-form-trigger-click');
39585     },
39586
39587     // private
39588     onDestroy : function(){
39589         if(this.trigger){
39590             this.trigger.removeAllListeners();
39591             this.trigger.remove();
39592         }
39593         if(this.wrap){
39594             this.wrap.remove();
39595         }
39596         Roo.form.TriggerField.superclass.onDestroy.call(this);
39597     },
39598
39599     // private
39600     onFocus : function(){
39601         Roo.form.TriggerField.superclass.onFocus.call(this);
39602         if(!this.mimicing){
39603             this.wrap.addClass('x-trigger-wrap-focus');
39604             this.mimicing = true;
39605             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39606             if(this.monitorTab){
39607                 this.el.on("keydown", this.checkTab, this);
39608             }
39609         }
39610     },
39611
39612     // private
39613     checkTab : function(e){
39614         if(e.getKey() == e.TAB){
39615             this.triggerBlur();
39616         }
39617     },
39618
39619     // private
39620     onBlur : function(){
39621         // do nothing
39622     },
39623
39624     // private
39625     mimicBlur : function(e, t){
39626         if(!this.wrap.contains(t) && this.validateBlur()){
39627             this.triggerBlur();
39628         }
39629     },
39630
39631     // private
39632     triggerBlur : function(){
39633         this.mimicing = false;
39634         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39635         if(this.monitorTab){
39636             this.el.un("keydown", this.checkTab, this);
39637         }
39638         this.wrap.removeClass('x-trigger-wrap-focus');
39639         Roo.form.TriggerField.superclass.onBlur.call(this);
39640     },
39641
39642     // private
39643     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39644     validateBlur : function(e, t){
39645         return true;
39646     },
39647
39648     // private
39649     onDisable : function(){
39650         Roo.form.TriggerField.superclass.onDisable.call(this);
39651         if(this.wrap){
39652             this.wrap.addClass('x-item-disabled');
39653         }
39654     },
39655
39656     // private
39657     onEnable : function(){
39658         Roo.form.TriggerField.superclass.onEnable.call(this);
39659         if(this.wrap){
39660             this.wrap.removeClass('x-item-disabled');
39661         }
39662     },
39663
39664     // private
39665     onShow : function(){
39666         var ae = this.getActionEl();
39667         
39668         if(ae){
39669             ae.dom.style.display = '';
39670             ae.dom.style.visibility = 'visible';
39671         }
39672     },
39673
39674     // private
39675     
39676     onHide : function(){
39677         var ae = this.getActionEl();
39678         ae.dom.style.display = 'none';
39679     },
39680
39681     /**
39682      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39683      * by an implementing function.
39684      * @method
39685      * @param {EventObject} e
39686      */
39687     onTriggerClick : Roo.emptyFn
39688 });
39689
39690 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39691 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39692 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39693 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39694     initComponent : function(){
39695         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39696
39697         this.triggerConfig = {
39698             tag:'span', cls:'x-form-twin-triggers', cn:[
39699             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39700             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39701         ]};
39702     },
39703
39704     getTrigger : function(index){
39705         return this.triggers[index];
39706     },
39707
39708     initTrigger : function(){
39709         var ts = this.trigger.select('.x-form-trigger', true);
39710         this.wrap.setStyle('overflow', 'hidden');
39711         var triggerField = this;
39712         ts.each(function(t, all, index){
39713             t.hide = function(){
39714                 var w = triggerField.wrap.getWidth();
39715                 this.dom.style.display = 'none';
39716                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39717             };
39718             t.show = function(){
39719                 var w = triggerField.wrap.getWidth();
39720                 this.dom.style.display = '';
39721                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39722             };
39723             var triggerIndex = 'Trigger'+(index+1);
39724
39725             if(this['hide'+triggerIndex]){
39726                 t.dom.style.display = 'none';
39727             }
39728             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39729             t.addClassOnOver('x-form-trigger-over');
39730             t.addClassOnClick('x-form-trigger-click');
39731         }, this);
39732         this.triggers = ts.elements;
39733     },
39734
39735     onTrigger1Click : Roo.emptyFn,
39736     onTrigger2Click : Roo.emptyFn
39737 });/*
39738  * Based on:
39739  * Ext JS Library 1.1.1
39740  * Copyright(c) 2006-2007, Ext JS, LLC.
39741  *
39742  * Originally Released Under LGPL - original licence link has changed is not relivant.
39743  *
39744  * Fork - LGPL
39745  * <script type="text/javascript">
39746  */
39747  
39748 /**
39749  * @class Roo.form.TextArea
39750  * @extends Roo.form.TextField
39751  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39752  * support for auto-sizing.
39753  * @constructor
39754  * Creates a new TextArea
39755  * @param {Object} config Configuration options
39756  */
39757 Roo.form.TextArea = function(config){
39758     Roo.form.TextArea.superclass.constructor.call(this, config);
39759     // these are provided exchanges for backwards compat
39760     // minHeight/maxHeight were replaced by growMin/growMax to be
39761     // compatible with TextField growing config values
39762     if(this.minHeight !== undefined){
39763         this.growMin = this.minHeight;
39764     }
39765     if(this.maxHeight !== undefined){
39766         this.growMax = this.maxHeight;
39767     }
39768 };
39769
39770 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39771     /**
39772      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39773      */
39774     growMin : 60,
39775     /**
39776      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39777      */
39778     growMax: 1000,
39779     /**
39780      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39781      * in the field (equivalent to setting overflow: hidden, defaults to false)
39782      */
39783     preventScrollbars: false,
39784     /**
39785      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39786      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39787      */
39788
39789     // private
39790     onRender : function(ct, position){
39791         if(!this.el){
39792             this.defaultAutoCreate = {
39793                 tag: "textarea",
39794                 style:"width:300px;height:60px;",
39795                 autocomplete: "new-password"
39796             };
39797         }
39798         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39799         if(this.grow){
39800             this.textSizeEl = Roo.DomHelper.append(document.body, {
39801                 tag: "pre", cls: "x-form-grow-sizer"
39802             });
39803             if(this.preventScrollbars){
39804                 this.el.setStyle("overflow", "hidden");
39805             }
39806             this.el.setHeight(this.growMin);
39807         }
39808     },
39809
39810     onDestroy : function(){
39811         if(this.textSizeEl){
39812             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39813         }
39814         Roo.form.TextArea.superclass.onDestroy.call(this);
39815     },
39816
39817     // private
39818     onKeyUp : function(e){
39819         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39820             this.autoSize();
39821         }
39822     },
39823
39824     /**
39825      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39826      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39827      */
39828     autoSize : function(){
39829         if(!this.grow || !this.textSizeEl){
39830             return;
39831         }
39832         var el = this.el;
39833         var v = el.dom.value;
39834         var ts = this.textSizeEl;
39835
39836         ts.innerHTML = '';
39837         ts.appendChild(document.createTextNode(v));
39838         v = ts.innerHTML;
39839
39840         Roo.fly(ts).setWidth(this.el.getWidth());
39841         if(v.length < 1){
39842             v = "&#160;&#160;";
39843         }else{
39844             if(Roo.isIE){
39845                 v = v.replace(/\n/g, '<p>&#160;</p>');
39846             }
39847             v += "&#160;\n&#160;";
39848         }
39849         ts.innerHTML = v;
39850         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39851         if(h != this.lastHeight){
39852             this.lastHeight = h;
39853             this.el.setHeight(h);
39854             this.fireEvent("autosize", this, h);
39855         }
39856     }
39857 });/*
39858  * Based on:
39859  * Ext JS Library 1.1.1
39860  * Copyright(c) 2006-2007, Ext JS, LLC.
39861  *
39862  * Originally Released Under LGPL - original licence link has changed is not relivant.
39863  *
39864  * Fork - LGPL
39865  * <script type="text/javascript">
39866  */
39867  
39868
39869 /**
39870  * @class Roo.form.NumberField
39871  * @extends Roo.form.TextField
39872  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39873  * @constructor
39874  * Creates a new NumberField
39875  * @param {Object} config Configuration options
39876  */
39877 Roo.form.NumberField = function(config){
39878     Roo.form.NumberField.superclass.constructor.call(this, config);
39879 };
39880
39881 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39882     /**
39883      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39884      */
39885     fieldClass: "x-form-field x-form-num-field",
39886     /**
39887      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39888      */
39889     allowDecimals : true,
39890     /**
39891      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39892      */
39893     decimalSeparator : ".",
39894     /**
39895      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39896      */
39897     decimalPrecision : 2,
39898     /**
39899      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39900      */
39901     allowNegative : true,
39902     /**
39903      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39904      */
39905     minValue : Number.NEGATIVE_INFINITY,
39906     /**
39907      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39908      */
39909     maxValue : Number.MAX_VALUE,
39910     /**
39911      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39912      */
39913     minText : "The minimum value for this field is {0}",
39914     /**
39915      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39916      */
39917     maxText : "The maximum value for this field is {0}",
39918     /**
39919      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39920      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39921      */
39922     nanText : "{0} is not a valid number",
39923
39924     // private
39925     initEvents : function(){
39926         Roo.form.NumberField.superclass.initEvents.call(this);
39927         var allowed = "0123456789";
39928         if(this.allowDecimals){
39929             allowed += this.decimalSeparator;
39930         }
39931         if(this.allowNegative){
39932             allowed += "-";
39933         }
39934         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39935         var keyPress = function(e){
39936             var k = e.getKey();
39937             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39938                 return;
39939             }
39940             var c = e.getCharCode();
39941             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39942                 e.stopEvent();
39943             }
39944         };
39945         this.el.on("keypress", keyPress, this);
39946     },
39947
39948     // private
39949     validateValue : function(value){
39950         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39951             return false;
39952         }
39953         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39954              return true;
39955         }
39956         var num = this.parseValue(value);
39957         if(isNaN(num)){
39958             this.markInvalid(String.format(this.nanText, value));
39959             return false;
39960         }
39961         if(num < this.minValue){
39962             this.markInvalid(String.format(this.minText, this.minValue));
39963             return false;
39964         }
39965         if(num > this.maxValue){
39966             this.markInvalid(String.format(this.maxText, this.maxValue));
39967             return false;
39968         }
39969         return true;
39970     },
39971
39972     getValue : function(){
39973         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
39974     },
39975
39976     // private
39977     parseValue : function(value){
39978         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39979         return isNaN(value) ? '' : value;
39980     },
39981
39982     // private
39983     fixPrecision : function(value){
39984         var nan = isNaN(value);
39985         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39986             return nan ? '' : value;
39987         }
39988         return parseFloat(value).toFixed(this.decimalPrecision);
39989     },
39990
39991     setValue : function(v){
39992         v = this.fixPrecision(v);
39993         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
39994     },
39995
39996     // private
39997     decimalPrecisionFcn : function(v){
39998         return Math.floor(v);
39999     },
40000
40001     beforeBlur : function(){
40002         var v = this.parseValue(this.getRawValue());
40003         if(v){
40004             this.setValue(v);
40005         }
40006     }
40007 });/*
40008  * Based on:
40009  * Ext JS Library 1.1.1
40010  * Copyright(c) 2006-2007, Ext JS, LLC.
40011  *
40012  * Originally Released Under LGPL - original licence link has changed is not relivant.
40013  *
40014  * Fork - LGPL
40015  * <script type="text/javascript">
40016  */
40017  
40018 /**
40019  * @class Roo.form.DateField
40020  * @extends Roo.form.TriggerField
40021  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40022 * @constructor
40023 * Create a new DateField
40024 * @param {Object} config
40025  */
40026 Roo.form.DateField = function(config){
40027     Roo.form.DateField.superclass.constructor.call(this, config);
40028     
40029       this.addEvents({
40030          
40031         /**
40032          * @event select
40033          * Fires when a date is selected
40034              * @param {Roo.form.DateField} combo This combo box
40035              * @param {Date} date The date selected
40036              */
40037         'select' : true
40038          
40039     });
40040     
40041     
40042     if(typeof this.minValue == "string") {
40043         this.minValue = this.parseDate(this.minValue);
40044     }
40045     if(typeof this.maxValue == "string") {
40046         this.maxValue = this.parseDate(this.maxValue);
40047     }
40048     this.ddMatch = null;
40049     if(this.disabledDates){
40050         var dd = this.disabledDates;
40051         var re = "(?:";
40052         for(var i = 0; i < dd.length; i++){
40053             re += dd[i];
40054             if(i != dd.length-1) {
40055                 re += "|";
40056             }
40057         }
40058         this.ddMatch = new RegExp(re + ")");
40059     }
40060 };
40061
40062 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40063     /**
40064      * @cfg {String} format
40065      * The default date format string which can be overriden for localization support.  The format must be
40066      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40067      */
40068     format : "m/d/y",
40069     /**
40070      * @cfg {String} altFormats
40071      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40072      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40073      */
40074     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40075     /**
40076      * @cfg {Array} disabledDays
40077      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40078      */
40079     disabledDays : null,
40080     /**
40081      * @cfg {String} disabledDaysText
40082      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40083      */
40084     disabledDaysText : "Disabled",
40085     /**
40086      * @cfg {Array} disabledDates
40087      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40088      * expression so they are very powerful. Some examples:
40089      * <ul>
40090      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40091      * <li>["03/08", "09/16"] would disable those days for every year</li>
40092      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40093      * <li>["03/../2006"] would disable every day in March 2006</li>
40094      * <li>["^03"] would disable every day in every March</li>
40095      * </ul>
40096      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40097      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40098      */
40099     disabledDates : null,
40100     /**
40101      * @cfg {String} disabledDatesText
40102      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40103      */
40104     disabledDatesText : "Disabled",
40105     /**
40106      * @cfg {Date/String} minValue
40107      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40108      * valid format (defaults to null).
40109      */
40110     minValue : null,
40111     /**
40112      * @cfg {Date/String} maxValue
40113      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40114      * valid format (defaults to null).
40115      */
40116     maxValue : null,
40117     /**
40118      * @cfg {String} minText
40119      * The error text to display when the date in the cell is before minValue (defaults to
40120      * 'The date in this field must be after {minValue}').
40121      */
40122     minText : "The date in this field must be equal to or after {0}",
40123     /**
40124      * @cfg {String} maxText
40125      * The error text to display when the date in the cell is after maxValue (defaults to
40126      * 'The date in this field must be before {maxValue}').
40127      */
40128     maxText : "The date in this field must be equal to or before {0}",
40129     /**
40130      * @cfg {String} invalidText
40131      * The error text to display when the date in the field is invalid (defaults to
40132      * '{value} is not a valid date - it must be in the format {format}').
40133      */
40134     invalidText : "{0} is not a valid date - it must be in the format {1}",
40135     /**
40136      * @cfg {String} triggerClass
40137      * An additional CSS class used to style the trigger button.  The trigger will always get the
40138      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40139      * which displays a calendar icon).
40140      */
40141     triggerClass : 'x-form-date-trigger',
40142     
40143
40144     /**
40145      * @cfg {Boolean} useIso
40146      * if enabled, then the date field will use a hidden field to store the 
40147      * real value as iso formated date. default (false)
40148      */ 
40149     useIso : false,
40150     /**
40151      * @cfg {String/Object} autoCreate
40152      * A DomHelper element spec, or true for a default element spec (defaults to
40153      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40154      */ 
40155     // private
40156     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40157     
40158     // private
40159     hiddenField: false,
40160     
40161     onRender : function(ct, position)
40162     {
40163         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40164         if (this.useIso) {
40165             //this.el.dom.removeAttribute('name'); 
40166             Roo.log("Changing name?");
40167             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40168             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40169                     'before', true);
40170             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40171             // prevent input submission
40172             this.hiddenName = this.name;
40173         }
40174             
40175             
40176     },
40177     
40178     // private
40179     validateValue : function(value)
40180     {
40181         value = this.formatDate(value);
40182         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40183             Roo.log('super failed');
40184             return false;
40185         }
40186         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40187              return true;
40188         }
40189         var svalue = value;
40190         value = this.parseDate(value);
40191         if(!value){
40192             Roo.log('parse date failed' + svalue);
40193             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40194             return false;
40195         }
40196         var time = value.getTime();
40197         if(this.minValue && time < this.minValue.getTime()){
40198             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40199             return false;
40200         }
40201         if(this.maxValue && time > this.maxValue.getTime()){
40202             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40203             return false;
40204         }
40205         if(this.disabledDays){
40206             var day = value.getDay();
40207             for(var i = 0; i < this.disabledDays.length; i++) {
40208                 if(day === this.disabledDays[i]){
40209                     this.markInvalid(this.disabledDaysText);
40210                     return false;
40211                 }
40212             }
40213         }
40214         var fvalue = this.formatDate(value);
40215         if(this.ddMatch && this.ddMatch.test(fvalue)){
40216             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40217             return false;
40218         }
40219         return true;
40220     },
40221
40222     // private
40223     // Provides logic to override the default TriggerField.validateBlur which just returns true
40224     validateBlur : function(){
40225         return !this.menu || !this.menu.isVisible();
40226     },
40227     
40228     getName: function()
40229     {
40230         // returns hidden if it's set..
40231         if (!this.rendered) {return ''};
40232         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40233         
40234     },
40235
40236     /**
40237      * Returns the current date value of the date field.
40238      * @return {Date} The date value
40239      */
40240     getValue : function(){
40241         
40242         return  this.hiddenField ?
40243                 this.hiddenField.value :
40244                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40245     },
40246
40247     /**
40248      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40249      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40250      * (the default format used is "m/d/y").
40251      * <br />Usage:
40252      * <pre><code>
40253 //All of these calls set the same date value (May 4, 2006)
40254
40255 //Pass a date object:
40256 var dt = new Date('5/4/06');
40257 dateField.setValue(dt);
40258
40259 //Pass a date string (default format):
40260 dateField.setValue('5/4/06');
40261
40262 //Pass a date string (custom format):
40263 dateField.format = 'Y-m-d';
40264 dateField.setValue('2006-5-4');
40265 </code></pre>
40266      * @param {String/Date} date The date or valid date string
40267      */
40268     setValue : function(date){
40269         if (this.hiddenField) {
40270             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40271         }
40272         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40273         // make sure the value field is always stored as a date..
40274         this.value = this.parseDate(date);
40275         
40276         
40277     },
40278
40279     // private
40280     parseDate : function(value){
40281         if(!value || value instanceof Date){
40282             return value;
40283         }
40284         var v = Date.parseDate(value, this.format);
40285          if (!v && this.useIso) {
40286             v = Date.parseDate(value, 'Y-m-d');
40287         }
40288         if(!v && this.altFormats){
40289             if(!this.altFormatsArray){
40290                 this.altFormatsArray = this.altFormats.split("|");
40291             }
40292             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40293                 v = Date.parseDate(value, this.altFormatsArray[i]);
40294             }
40295         }
40296         return v;
40297     },
40298
40299     // private
40300     formatDate : function(date, fmt){
40301         return (!date || !(date instanceof Date)) ?
40302                date : date.dateFormat(fmt || this.format);
40303     },
40304
40305     // private
40306     menuListeners : {
40307         select: function(m, d){
40308             
40309             this.setValue(d);
40310             this.fireEvent('select', this, d);
40311         },
40312         show : function(){ // retain focus styling
40313             this.onFocus();
40314         },
40315         hide : function(){
40316             this.focus.defer(10, this);
40317             var ml = this.menuListeners;
40318             this.menu.un("select", ml.select,  this);
40319             this.menu.un("show", ml.show,  this);
40320             this.menu.un("hide", ml.hide,  this);
40321         }
40322     },
40323
40324     // private
40325     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40326     onTriggerClick : function(){
40327         if(this.disabled){
40328             return;
40329         }
40330         if(this.menu == null){
40331             this.menu = new Roo.menu.DateMenu();
40332         }
40333         Roo.apply(this.menu.picker,  {
40334             showClear: this.allowBlank,
40335             minDate : this.minValue,
40336             maxDate : this.maxValue,
40337             disabledDatesRE : this.ddMatch,
40338             disabledDatesText : this.disabledDatesText,
40339             disabledDays : this.disabledDays,
40340             disabledDaysText : this.disabledDaysText,
40341             format : this.useIso ? 'Y-m-d' : this.format,
40342             minText : String.format(this.minText, this.formatDate(this.minValue)),
40343             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40344         });
40345         this.menu.on(Roo.apply({}, this.menuListeners, {
40346             scope:this
40347         }));
40348         this.menu.picker.setValue(this.getValue() || new Date());
40349         this.menu.show(this.el, "tl-bl?");
40350     },
40351
40352     beforeBlur : function(){
40353         var v = this.parseDate(this.getRawValue());
40354         if(v){
40355             this.setValue(v);
40356         }
40357     },
40358
40359     /*@
40360      * overide
40361      * 
40362      */
40363     isDirty : function() {
40364         if(this.disabled) {
40365             return false;
40366         }
40367         
40368         if(typeof(this.startValue) === 'undefined'){
40369             return false;
40370         }
40371         
40372         return String(this.getValue()) !== String(this.startValue);
40373         
40374     }
40375 });/*
40376  * Based on:
40377  * Ext JS Library 1.1.1
40378  * Copyright(c) 2006-2007, Ext JS, LLC.
40379  *
40380  * Originally Released Under LGPL - original licence link has changed is not relivant.
40381  *
40382  * Fork - LGPL
40383  * <script type="text/javascript">
40384  */
40385  
40386 /**
40387  * @class Roo.form.MonthField
40388  * @extends Roo.form.TriggerField
40389  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40390 * @constructor
40391 * Create a new MonthField
40392 * @param {Object} config
40393  */
40394 Roo.form.MonthField = function(config){
40395     
40396     Roo.form.MonthField.superclass.constructor.call(this, config);
40397     
40398       this.addEvents({
40399          
40400         /**
40401          * @event select
40402          * Fires when a date is selected
40403              * @param {Roo.form.MonthFieeld} combo This combo box
40404              * @param {Date} date The date selected
40405              */
40406         'select' : true
40407          
40408     });
40409     
40410     
40411     if(typeof this.minValue == "string") {
40412         this.minValue = this.parseDate(this.minValue);
40413     }
40414     if(typeof this.maxValue == "string") {
40415         this.maxValue = this.parseDate(this.maxValue);
40416     }
40417     this.ddMatch = null;
40418     if(this.disabledDates){
40419         var dd = this.disabledDates;
40420         var re = "(?:";
40421         for(var i = 0; i < dd.length; i++){
40422             re += dd[i];
40423             if(i != dd.length-1) {
40424                 re += "|";
40425             }
40426         }
40427         this.ddMatch = new RegExp(re + ")");
40428     }
40429 };
40430
40431 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40432     /**
40433      * @cfg {String} format
40434      * The default date format string which can be overriden for localization support.  The format must be
40435      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40436      */
40437     format : "M Y",
40438     /**
40439      * @cfg {String} altFormats
40440      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40441      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40442      */
40443     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40444     /**
40445      * @cfg {Array} disabledDays
40446      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40447      */
40448     disabledDays : [0,1,2,3,4,5,6],
40449     /**
40450      * @cfg {String} disabledDaysText
40451      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40452      */
40453     disabledDaysText : "Disabled",
40454     /**
40455      * @cfg {Array} disabledDates
40456      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40457      * expression so they are very powerful. Some examples:
40458      * <ul>
40459      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40460      * <li>["03/08", "09/16"] would disable those days for every year</li>
40461      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40462      * <li>["03/../2006"] would disable every day in March 2006</li>
40463      * <li>["^03"] would disable every day in every March</li>
40464      * </ul>
40465      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40466      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40467      */
40468     disabledDates : null,
40469     /**
40470      * @cfg {String} disabledDatesText
40471      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40472      */
40473     disabledDatesText : "Disabled",
40474     /**
40475      * @cfg {Date/String} minValue
40476      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40477      * valid format (defaults to null).
40478      */
40479     minValue : null,
40480     /**
40481      * @cfg {Date/String} maxValue
40482      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40483      * valid format (defaults to null).
40484      */
40485     maxValue : null,
40486     /**
40487      * @cfg {String} minText
40488      * The error text to display when the date in the cell is before minValue (defaults to
40489      * 'The date in this field must be after {minValue}').
40490      */
40491     minText : "The date in this field must be equal to or after {0}",
40492     /**
40493      * @cfg {String} maxTextf
40494      * The error text to display when the date in the cell is after maxValue (defaults to
40495      * 'The date in this field must be before {maxValue}').
40496      */
40497     maxText : "The date in this field must be equal to or before {0}",
40498     /**
40499      * @cfg {String} invalidText
40500      * The error text to display when the date in the field is invalid (defaults to
40501      * '{value} is not a valid date - it must be in the format {format}').
40502      */
40503     invalidText : "{0} is not a valid date - it must be in the format {1}",
40504     /**
40505      * @cfg {String} triggerClass
40506      * An additional CSS class used to style the trigger button.  The trigger will always get the
40507      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40508      * which displays a calendar icon).
40509      */
40510     triggerClass : 'x-form-date-trigger',
40511     
40512
40513     /**
40514      * @cfg {Boolean} useIso
40515      * if enabled, then the date field will use a hidden field to store the 
40516      * real value as iso formated date. default (true)
40517      */ 
40518     useIso : true,
40519     /**
40520      * @cfg {String/Object} autoCreate
40521      * A DomHelper element spec, or true for a default element spec (defaults to
40522      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40523      */ 
40524     // private
40525     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40526     
40527     // private
40528     hiddenField: false,
40529     
40530     hideMonthPicker : false,
40531     
40532     onRender : function(ct, position)
40533     {
40534         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40535         if (this.useIso) {
40536             this.el.dom.removeAttribute('name'); 
40537             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40538                     'before', true);
40539             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40540             // prevent input submission
40541             this.hiddenName = this.name;
40542         }
40543             
40544             
40545     },
40546     
40547     // private
40548     validateValue : function(value)
40549     {
40550         value = this.formatDate(value);
40551         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40552             return false;
40553         }
40554         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40555              return true;
40556         }
40557         var svalue = value;
40558         value = this.parseDate(value);
40559         if(!value){
40560             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40561             return false;
40562         }
40563         var time = value.getTime();
40564         if(this.minValue && time < this.minValue.getTime()){
40565             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40566             return false;
40567         }
40568         if(this.maxValue && time > this.maxValue.getTime()){
40569             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40570             return false;
40571         }
40572         /*if(this.disabledDays){
40573             var day = value.getDay();
40574             for(var i = 0; i < this.disabledDays.length; i++) {
40575                 if(day === this.disabledDays[i]){
40576                     this.markInvalid(this.disabledDaysText);
40577                     return false;
40578                 }
40579             }
40580         }
40581         */
40582         var fvalue = this.formatDate(value);
40583         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40584             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40585             return false;
40586         }
40587         */
40588         return true;
40589     },
40590
40591     // private
40592     // Provides logic to override the default TriggerField.validateBlur which just returns true
40593     validateBlur : function(){
40594         return !this.menu || !this.menu.isVisible();
40595     },
40596
40597     /**
40598      * Returns the current date value of the date field.
40599      * @return {Date} The date value
40600      */
40601     getValue : function(){
40602         
40603         
40604         
40605         return  this.hiddenField ?
40606                 this.hiddenField.value :
40607                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40608     },
40609
40610     /**
40611      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40612      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40613      * (the default format used is "m/d/y").
40614      * <br />Usage:
40615      * <pre><code>
40616 //All of these calls set the same date value (May 4, 2006)
40617
40618 //Pass a date object:
40619 var dt = new Date('5/4/06');
40620 monthField.setValue(dt);
40621
40622 //Pass a date string (default format):
40623 monthField.setValue('5/4/06');
40624
40625 //Pass a date string (custom format):
40626 monthField.format = 'Y-m-d';
40627 monthField.setValue('2006-5-4');
40628 </code></pre>
40629      * @param {String/Date} date The date or valid date string
40630      */
40631     setValue : function(date){
40632         Roo.log('month setValue' + date);
40633         // can only be first of month..
40634         
40635         var val = this.parseDate(date);
40636         
40637         if (this.hiddenField) {
40638             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40639         }
40640         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40641         this.value = this.parseDate(date);
40642     },
40643
40644     // private
40645     parseDate : function(value){
40646         if(!value || value instanceof Date){
40647             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40648             return value;
40649         }
40650         var v = Date.parseDate(value, this.format);
40651         if (!v && this.useIso) {
40652             v = Date.parseDate(value, 'Y-m-d');
40653         }
40654         if (v) {
40655             // 
40656             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40657         }
40658         
40659         
40660         if(!v && this.altFormats){
40661             if(!this.altFormatsArray){
40662                 this.altFormatsArray = this.altFormats.split("|");
40663             }
40664             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40665                 v = Date.parseDate(value, this.altFormatsArray[i]);
40666             }
40667         }
40668         return v;
40669     },
40670
40671     // private
40672     formatDate : function(date, fmt){
40673         return (!date || !(date instanceof Date)) ?
40674                date : date.dateFormat(fmt || this.format);
40675     },
40676
40677     // private
40678     menuListeners : {
40679         select: function(m, d){
40680             this.setValue(d);
40681             this.fireEvent('select', this, d);
40682         },
40683         show : function(){ // retain focus styling
40684             this.onFocus();
40685         },
40686         hide : function(){
40687             this.focus.defer(10, this);
40688             var ml = this.menuListeners;
40689             this.menu.un("select", ml.select,  this);
40690             this.menu.un("show", ml.show,  this);
40691             this.menu.un("hide", ml.hide,  this);
40692         }
40693     },
40694     // private
40695     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40696     onTriggerClick : function(){
40697         if(this.disabled){
40698             return;
40699         }
40700         if(this.menu == null){
40701             this.menu = new Roo.menu.DateMenu();
40702            
40703         }
40704         
40705         Roo.apply(this.menu.picker,  {
40706             
40707             showClear: this.allowBlank,
40708             minDate : this.minValue,
40709             maxDate : this.maxValue,
40710             disabledDatesRE : this.ddMatch,
40711             disabledDatesText : this.disabledDatesText,
40712             
40713             format : this.useIso ? 'Y-m-d' : this.format,
40714             minText : String.format(this.minText, this.formatDate(this.minValue)),
40715             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40716             
40717         });
40718          this.menu.on(Roo.apply({}, this.menuListeners, {
40719             scope:this
40720         }));
40721        
40722         
40723         var m = this.menu;
40724         var p = m.picker;
40725         
40726         // hide month picker get's called when we called by 'before hide';
40727         
40728         var ignorehide = true;
40729         p.hideMonthPicker  = function(disableAnim){
40730             if (ignorehide) {
40731                 return;
40732             }
40733              if(this.monthPicker){
40734                 Roo.log("hideMonthPicker called");
40735                 if(disableAnim === true){
40736                     this.monthPicker.hide();
40737                 }else{
40738                     this.monthPicker.slideOut('t', {duration:.2});
40739                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40740                     p.fireEvent("select", this, this.value);
40741                     m.hide();
40742                 }
40743             }
40744         }
40745         
40746         Roo.log('picker set value');
40747         Roo.log(this.getValue());
40748         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40749         m.show(this.el, 'tl-bl?');
40750         ignorehide  = false;
40751         // this will trigger hideMonthPicker..
40752         
40753         
40754         // hidden the day picker
40755         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40756         
40757         
40758         
40759       
40760         
40761         p.showMonthPicker.defer(100, p);
40762     
40763         
40764        
40765     },
40766
40767     beforeBlur : function(){
40768         var v = this.parseDate(this.getRawValue());
40769         if(v){
40770             this.setValue(v);
40771         }
40772     }
40773
40774     /** @cfg {Boolean} grow @hide */
40775     /** @cfg {Number} growMin @hide */
40776     /** @cfg {Number} growMax @hide */
40777     /**
40778      * @hide
40779      * @method autoSize
40780      */
40781 });/*
40782  * Based on:
40783  * Ext JS Library 1.1.1
40784  * Copyright(c) 2006-2007, Ext JS, LLC.
40785  *
40786  * Originally Released Under LGPL - original licence link has changed is not relivant.
40787  *
40788  * Fork - LGPL
40789  * <script type="text/javascript">
40790  */
40791  
40792
40793 /**
40794  * @class Roo.form.ComboBox
40795  * @extends Roo.form.TriggerField
40796  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40797  * @constructor
40798  * Create a new ComboBox.
40799  * @param {Object} config Configuration options
40800  */
40801 Roo.form.ComboBox = function(config){
40802     Roo.form.ComboBox.superclass.constructor.call(this, config);
40803     this.addEvents({
40804         /**
40805          * @event expand
40806          * Fires when the dropdown list is expanded
40807              * @param {Roo.form.ComboBox} combo This combo box
40808              */
40809         'expand' : true,
40810         /**
40811          * @event collapse
40812          * Fires when the dropdown list is collapsed
40813              * @param {Roo.form.ComboBox} combo This combo box
40814              */
40815         'collapse' : true,
40816         /**
40817          * @event beforeselect
40818          * Fires before a list item is selected. Return false to cancel the selection.
40819              * @param {Roo.form.ComboBox} combo This combo box
40820              * @param {Roo.data.Record} record The data record returned from the underlying store
40821              * @param {Number} index The index of the selected item in the dropdown list
40822              */
40823         'beforeselect' : true,
40824         /**
40825          * @event select
40826          * Fires when a list item is selected
40827              * @param {Roo.form.ComboBox} combo This combo box
40828              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40829              * @param {Number} index The index of the selected item in the dropdown list
40830              */
40831         'select' : true,
40832         /**
40833          * @event beforequery
40834          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40835          * The event object passed has these properties:
40836              * @param {Roo.form.ComboBox} combo This combo box
40837              * @param {String} query The query
40838              * @param {Boolean} forceAll true to force "all" query
40839              * @param {Boolean} cancel true to cancel the query
40840              * @param {Object} e The query event object
40841              */
40842         'beforequery': true,
40843          /**
40844          * @event add
40845          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40846              * @param {Roo.form.ComboBox} combo This combo box
40847              */
40848         'add' : true,
40849         /**
40850          * @event edit
40851          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40852              * @param {Roo.form.ComboBox} combo This combo box
40853              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40854              */
40855         'edit' : true
40856         
40857         
40858     });
40859     if(this.transform){
40860         this.allowDomMove = false;
40861         var s = Roo.getDom(this.transform);
40862         if(!this.hiddenName){
40863             this.hiddenName = s.name;
40864         }
40865         if(!this.store){
40866             this.mode = 'local';
40867             var d = [], opts = s.options;
40868             for(var i = 0, len = opts.length;i < len; i++){
40869                 var o = opts[i];
40870                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40871                 if(o.selected) {
40872                     this.value = value;
40873                 }
40874                 d.push([value, o.text]);
40875             }
40876             this.store = new Roo.data.SimpleStore({
40877                 'id': 0,
40878                 fields: ['value', 'text'],
40879                 data : d
40880             });
40881             this.valueField = 'value';
40882             this.displayField = 'text';
40883         }
40884         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40885         if(!this.lazyRender){
40886             this.target = true;
40887             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40888             s.parentNode.removeChild(s); // remove it
40889             this.render(this.el.parentNode);
40890         }else{
40891             s.parentNode.removeChild(s); // remove it
40892         }
40893
40894     }
40895     if (this.store) {
40896         this.store = Roo.factory(this.store, Roo.data);
40897     }
40898     
40899     this.selectedIndex = -1;
40900     if(this.mode == 'local'){
40901         if(config.queryDelay === undefined){
40902             this.queryDelay = 10;
40903         }
40904         if(config.minChars === undefined){
40905             this.minChars = 0;
40906         }
40907     }
40908 };
40909
40910 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40911     /**
40912      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40913      */
40914     /**
40915      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40916      * rendering into an Roo.Editor, defaults to false)
40917      */
40918     /**
40919      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40920      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40921      */
40922     /**
40923      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40924      */
40925     /**
40926      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40927      * the dropdown list (defaults to undefined, with no header element)
40928      */
40929
40930      /**
40931      * @cfg {String/Roo.Template} tpl The template to use to render the output
40932      */
40933      
40934     // private
40935     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40936     /**
40937      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40938      */
40939     listWidth: undefined,
40940     /**
40941      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40942      * mode = 'remote' or 'text' if mode = 'local')
40943      */
40944     displayField: undefined,
40945     /**
40946      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40947      * mode = 'remote' or 'value' if mode = 'local'). 
40948      * Note: use of a valueField requires the user make a selection
40949      * in order for a value to be mapped.
40950      */
40951     valueField: undefined,
40952     
40953     
40954     /**
40955      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
40956      * field's data value (defaults to the underlying DOM element's name)
40957      */
40958     hiddenName: undefined,
40959     /**
40960      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
40961      */
40962     listClass: '',
40963     /**
40964      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
40965      */
40966     selectedClass: 'x-combo-selected',
40967     /**
40968      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40969      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
40970      * which displays a downward arrow icon).
40971      */
40972     triggerClass : 'x-form-arrow-trigger',
40973     /**
40974      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
40975      */
40976     shadow:'sides',
40977     /**
40978      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
40979      * anchor positions (defaults to 'tl-bl')
40980      */
40981     listAlign: 'tl-bl?',
40982     /**
40983      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
40984      */
40985     maxHeight: 300,
40986     /**
40987      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
40988      * query specified by the allQuery config option (defaults to 'query')
40989      */
40990     triggerAction: 'query',
40991     /**
40992      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
40993      * (defaults to 4, does not apply if editable = false)
40994      */
40995     minChars : 4,
40996     /**
40997      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
40998      * delay (typeAheadDelay) if it matches a known value (defaults to false)
40999      */
41000     typeAhead: false,
41001     /**
41002      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41003      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41004      */
41005     queryDelay: 500,
41006     /**
41007      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41008      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41009      */
41010     pageSize: 0,
41011     /**
41012      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41013      * when editable = true (defaults to false)
41014      */
41015     selectOnFocus:false,
41016     /**
41017      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41018      */
41019     queryParam: 'query',
41020     /**
41021      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41022      * when mode = 'remote' (defaults to 'Loading...')
41023      */
41024     loadingText: 'Loading...',
41025     /**
41026      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41027      */
41028     resizable: false,
41029     /**
41030      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41031      */
41032     handleHeight : 8,
41033     /**
41034      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41035      * traditional select (defaults to true)
41036      */
41037     editable: true,
41038     /**
41039      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41040      */
41041     allQuery: '',
41042     /**
41043      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41044      */
41045     mode: 'remote',
41046     /**
41047      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41048      * listWidth has a higher value)
41049      */
41050     minListWidth : 70,
41051     /**
41052      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41053      * allow the user to set arbitrary text into the field (defaults to false)
41054      */
41055     forceSelection:false,
41056     /**
41057      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41058      * if typeAhead = true (defaults to 250)
41059      */
41060     typeAheadDelay : 250,
41061     /**
41062      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41063      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41064      */
41065     valueNotFoundText : undefined,
41066     /**
41067      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41068      */
41069     blockFocus : false,
41070     
41071     /**
41072      * @cfg {Boolean} disableClear Disable showing of clear button.
41073      */
41074     disableClear : false,
41075     /**
41076      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41077      */
41078     alwaysQuery : false,
41079     
41080     //private
41081     addicon : false,
41082     editicon: false,
41083     
41084     // element that contains real text value.. (when hidden is used..)
41085      
41086     // private
41087     onRender : function(ct, position){
41088         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41089         if(this.hiddenName){
41090             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41091                     'before', true);
41092             this.hiddenField.value =
41093                 this.hiddenValue !== undefined ? this.hiddenValue :
41094                 this.value !== undefined ? this.value : '';
41095
41096             // prevent input submission
41097             this.el.dom.removeAttribute('name');
41098              
41099              
41100         }
41101         if(Roo.isGecko){
41102             this.el.dom.setAttribute('autocomplete', 'off');
41103         }
41104
41105         var cls = 'x-combo-list';
41106
41107         this.list = new Roo.Layer({
41108             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41109         });
41110
41111         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41112         this.list.setWidth(lw);
41113         this.list.swallowEvent('mousewheel');
41114         this.assetHeight = 0;
41115
41116         if(this.title){
41117             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41118             this.assetHeight += this.header.getHeight();
41119         }
41120
41121         this.innerList = this.list.createChild({cls:cls+'-inner'});
41122         this.innerList.on('mouseover', this.onViewOver, this);
41123         this.innerList.on('mousemove', this.onViewMove, this);
41124         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41125         
41126         if(this.allowBlank && !this.pageSize && !this.disableClear){
41127             this.footer = this.list.createChild({cls:cls+'-ft'});
41128             this.pageTb = new Roo.Toolbar(this.footer);
41129            
41130         }
41131         if(this.pageSize){
41132             this.footer = this.list.createChild({cls:cls+'-ft'});
41133             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41134                     {pageSize: this.pageSize});
41135             
41136         }
41137         
41138         if (this.pageTb && this.allowBlank && !this.disableClear) {
41139             var _this = this;
41140             this.pageTb.add(new Roo.Toolbar.Fill(), {
41141                 cls: 'x-btn-icon x-btn-clear',
41142                 text: '&#160;',
41143                 handler: function()
41144                 {
41145                     _this.collapse();
41146                     _this.clearValue();
41147                     _this.onSelect(false, -1);
41148                 }
41149             });
41150         }
41151         if (this.footer) {
41152             this.assetHeight += this.footer.getHeight();
41153         }
41154         
41155
41156         if(!this.tpl){
41157             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41158         }
41159
41160         this.view = new Roo.View(this.innerList, this.tpl, {
41161             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41162         });
41163
41164         this.view.on('click', this.onViewClick, this);
41165
41166         this.store.on('beforeload', this.onBeforeLoad, this);
41167         this.store.on('load', this.onLoad, this);
41168         this.store.on('loadexception', this.onLoadException, this);
41169
41170         if(this.resizable){
41171             this.resizer = new Roo.Resizable(this.list,  {
41172                pinned:true, handles:'se'
41173             });
41174             this.resizer.on('resize', function(r, w, h){
41175                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41176                 this.listWidth = w;
41177                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41178                 this.restrictHeight();
41179             }, this);
41180             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41181         }
41182         if(!this.editable){
41183             this.editable = true;
41184             this.setEditable(false);
41185         }  
41186         
41187         
41188         if (typeof(this.events.add.listeners) != 'undefined') {
41189             
41190             this.addicon = this.wrap.createChild(
41191                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41192        
41193             this.addicon.on('click', function(e) {
41194                 this.fireEvent('add', this);
41195             }, this);
41196         }
41197         if (typeof(this.events.edit.listeners) != 'undefined') {
41198             
41199             this.editicon = this.wrap.createChild(
41200                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41201             if (this.addicon) {
41202                 this.editicon.setStyle('margin-left', '40px');
41203             }
41204             this.editicon.on('click', function(e) {
41205                 
41206                 // we fire even  if inothing is selected..
41207                 this.fireEvent('edit', this, this.lastData );
41208                 
41209             }, this);
41210         }
41211         
41212         
41213         
41214     },
41215
41216     // private
41217     initEvents : function(){
41218         Roo.form.ComboBox.superclass.initEvents.call(this);
41219
41220         this.keyNav = new Roo.KeyNav(this.el, {
41221             "up" : function(e){
41222                 this.inKeyMode = true;
41223                 this.selectPrev();
41224             },
41225
41226             "down" : function(e){
41227                 if(!this.isExpanded()){
41228                     this.onTriggerClick();
41229                 }else{
41230                     this.inKeyMode = true;
41231                     this.selectNext();
41232                 }
41233             },
41234
41235             "enter" : function(e){
41236                 this.onViewClick();
41237                 //return true;
41238             },
41239
41240             "esc" : function(e){
41241                 this.collapse();
41242             },
41243
41244             "tab" : function(e){
41245                 this.onViewClick(false);
41246                 this.fireEvent("specialkey", this, e);
41247                 return true;
41248             },
41249
41250             scope : this,
41251
41252             doRelay : function(foo, bar, hname){
41253                 if(hname == 'down' || this.scope.isExpanded()){
41254                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41255                 }
41256                 return true;
41257             },
41258
41259             forceKeyDown: true
41260         });
41261         this.queryDelay = Math.max(this.queryDelay || 10,
41262                 this.mode == 'local' ? 10 : 250);
41263         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41264         if(this.typeAhead){
41265             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41266         }
41267         if(this.editable !== false){
41268             this.el.on("keyup", this.onKeyUp, this);
41269         }
41270         if(this.forceSelection){
41271             this.on('blur', this.doForce, this);
41272         }
41273     },
41274
41275     onDestroy : function(){
41276         if(this.view){
41277             this.view.setStore(null);
41278             this.view.el.removeAllListeners();
41279             this.view.el.remove();
41280             this.view.purgeListeners();
41281         }
41282         if(this.list){
41283             this.list.destroy();
41284         }
41285         if(this.store){
41286             this.store.un('beforeload', this.onBeforeLoad, this);
41287             this.store.un('load', this.onLoad, this);
41288             this.store.un('loadexception', this.onLoadException, this);
41289         }
41290         Roo.form.ComboBox.superclass.onDestroy.call(this);
41291     },
41292
41293     // private
41294     fireKey : function(e){
41295         if(e.isNavKeyPress() && !this.list.isVisible()){
41296             this.fireEvent("specialkey", this, e);
41297         }
41298     },
41299
41300     // private
41301     onResize: function(w, h){
41302         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41303         
41304         if(typeof w != 'number'){
41305             // we do not handle it!?!?
41306             return;
41307         }
41308         var tw = this.trigger.getWidth();
41309         tw += this.addicon ? this.addicon.getWidth() : 0;
41310         tw += this.editicon ? this.editicon.getWidth() : 0;
41311         var x = w - tw;
41312         this.el.setWidth( this.adjustWidth('input', x));
41313             
41314         this.trigger.setStyle('left', x+'px');
41315         
41316         if(this.list && this.listWidth === undefined){
41317             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41318             this.list.setWidth(lw);
41319             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41320         }
41321         
41322     
41323         
41324     },
41325
41326     /**
41327      * Allow or prevent the user from directly editing the field text.  If false is passed,
41328      * the user will only be able to select from the items defined in the dropdown list.  This method
41329      * is the runtime equivalent of setting the 'editable' config option at config time.
41330      * @param {Boolean} value True to allow the user to directly edit the field text
41331      */
41332     setEditable : function(value){
41333         if(value == this.editable){
41334             return;
41335         }
41336         this.editable = value;
41337         if(!value){
41338             this.el.dom.setAttribute('readOnly', true);
41339             this.el.on('mousedown', this.onTriggerClick,  this);
41340             this.el.addClass('x-combo-noedit');
41341         }else{
41342             this.el.dom.setAttribute('readOnly', false);
41343             this.el.un('mousedown', this.onTriggerClick,  this);
41344             this.el.removeClass('x-combo-noedit');
41345         }
41346     },
41347
41348     // private
41349     onBeforeLoad : function(){
41350         if(!this.hasFocus){
41351             return;
41352         }
41353         this.innerList.update(this.loadingText ?
41354                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41355         this.restrictHeight();
41356         this.selectedIndex = -1;
41357     },
41358
41359     // private
41360     onLoad : function(){
41361         if(!this.hasFocus){
41362             return;
41363         }
41364         if(this.store.getCount() > 0){
41365             this.expand();
41366             this.restrictHeight();
41367             if(this.lastQuery == this.allQuery){
41368                 if(this.editable){
41369                     this.el.dom.select();
41370                 }
41371                 if(!this.selectByValue(this.value, true)){
41372                     this.select(0, true);
41373                 }
41374             }else{
41375                 this.selectNext();
41376                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41377                     this.taTask.delay(this.typeAheadDelay);
41378                 }
41379             }
41380         }else{
41381             this.onEmptyResults();
41382         }
41383         //this.el.focus();
41384     },
41385     // private
41386     onLoadException : function()
41387     {
41388         this.collapse();
41389         Roo.log(this.store.reader.jsonData);
41390         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41391             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41392         }
41393         
41394         
41395     },
41396     // private
41397     onTypeAhead : function(){
41398         if(this.store.getCount() > 0){
41399             var r = this.store.getAt(0);
41400             var newValue = r.data[this.displayField];
41401             var len = newValue.length;
41402             var selStart = this.getRawValue().length;
41403             if(selStart != len){
41404                 this.setRawValue(newValue);
41405                 this.selectText(selStart, newValue.length);
41406             }
41407         }
41408     },
41409
41410     // private
41411     onSelect : function(record, index){
41412         if(this.fireEvent('beforeselect', this, record, index) !== false){
41413             this.setFromData(index > -1 ? record.data : false);
41414             this.collapse();
41415             this.fireEvent('select', this, record, index);
41416         }
41417     },
41418
41419     /**
41420      * Returns the currently selected field value or empty string if no value is set.
41421      * @return {String} value The selected value
41422      */
41423     getValue : function(){
41424         if(this.valueField){
41425             return typeof this.value != 'undefined' ? this.value : '';
41426         }
41427         return Roo.form.ComboBox.superclass.getValue.call(this);
41428     },
41429
41430     /**
41431      * Clears any text/value currently set in the field
41432      */
41433     clearValue : function(){
41434         if(this.hiddenField){
41435             this.hiddenField.value = '';
41436         }
41437         this.value = '';
41438         this.setRawValue('');
41439         this.lastSelectionText = '';
41440         
41441     },
41442
41443     /**
41444      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41445      * will be displayed in the field.  If the value does not match the data value of an existing item,
41446      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41447      * Otherwise the field will be blank (although the value will still be set).
41448      * @param {String} value The value to match
41449      */
41450     setValue : function(v){
41451         var text = v;
41452         if(this.valueField){
41453             var r = this.findRecord(this.valueField, v);
41454             if(r){
41455                 text = r.data[this.displayField];
41456             }else if(this.valueNotFoundText !== undefined){
41457                 text = this.valueNotFoundText;
41458             }
41459         }
41460         this.lastSelectionText = text;
41461         if(this.hiddenField){
41462             this.hiddenField.value = v;
41463         }
41464         Roo.form.ComboBox.superclass.setValue.call(this, text);
41465         this.value = v;
41466     },
41467     /**
41468      * @property {Object} the last set data for the element
41469      */
41470     
41471     lastData : false,
41472     /**
41473      * Sets the value of the field based on a object which is related to the record format for the store.
41474      * @param {Object} value the value to set as. or false on reset?
41475      */
41476     setFromData : function(o){
41477         var dv = ''; // display value
41478         var vv = ''; // value value..
41479         this.lastData = o;
41480         if (this.displayField) {
41481             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41482         } else {
41483             // this is an error condition!!!
41484             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41485         }
41486         
41487         if(this.valueField){
41488             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41489         }
41490         if(this.hiddenField){
41491             this.hiddenField.value = vv;
41492             
41493             this.lastSelectionText = dv;
41494             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41495             this.value = vv;
41496             return;
41497         }
41498         // no hidden field.. - we store the value in 'value', but still display
41499         // display field!!!!
41500         this.lastSelectionText = dv;
41501         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41502         this.value = vv;
41503         
41504         
41505     },
41506     // private
41507     reset : function(){
41508         // overridden so that last data is reset..
41509         this.setValue(this.resetValue);
41510         this.clearInvalid();
41511         this.lastData = false;
41512         if (this.view) {
41513             this.view.clearSelections();
41514         }
41515     },
41516     // private
41517     findRecord : function(prop, value){
41518         var record;
41519         if(this.store.getCount() > 0){
41520             this.store.each(function(r){
41521                 if(r.data[prop] == value){
41522                     record = r;
41523                     return false;
41524                 }
41525                 return true;
41526             });
41527         }
41528         return record;
41529     },
41530     
41531     getName: function()
41532     {
41533         // returns hidden if it's set..
41534         if (!this.rendered) {return ''};
41535         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41536         
41537     },
41538     // private
41539     onViewMove : function(e, t){
41540         this.inKeyMode = false;
41541     },
41542
41543     // private
41544     onViewOver : function(e, t){
41545         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41546             return;
41547         }
41548         var item = this.view.findItemFromChild(t);
41549         if(item){
41550             var index = this.view.indexOf(item);
41551             this.select(index, false);
41552         }
41553     },
41554
41555     // private
41556     onViewClick : function(doFocus)
41557     {
41558         var index = this.view.getSelectedIndexes()[0];
41559         var r = this.store.getAt(index);
41560         if(r){
41561             this.onSelect(r, index);
41562         }
41563         if(doFocus !== false && !this.blockFocus){
41564             this.el.focus();
41565         }
41566     },
41567
41568     // private
41569     restrictHeight : function(){
41570         this.innerList.dom.style.height = '';
41571         var inner = this.innerList.dom;
41572         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41573         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41574         this.list.beginUpdate();
41575         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41576         this.list.alignTo(this.el, this.listAlign);
41577         this.list.endUpdate();
41578     },
41579
41580     // private
41581     onEmptyResults : function(){
41582         this.collapse();
41583     },
41584
41585     /**
41586      * Returns true if the dropdown list is expanded, else false.
41587      */
41588     isExpanded : function(){
41589         return this.list.isVisible();
41590     },
41591
41592     /**
41593      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41594      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41595      * @param {String} value The data value of the item to select
41596      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41597      * selected item if it is not currently in view (defaults to true)
41598      * @return {Boolean} True if the value matched an item in the list, else false
41599      */
41600     selectByValue : function(v, scrollIntoView){
41601         if(v !== undefined && v !== null){
41602             var r = this.findRecord(this.valueField || this.displayField, v);
41603             if(r){
41604                 this.select(this.store.indexOf(r), scrollIntoView);
41605                 return true;
41606             }
41607         }
41608         return false;
41609     },
41610
41611     /**
41612      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41613      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41614      * @param {Number} index The zero-based index of the list item to select
41615      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41616      * selected item if it is not currently in view (defaults to true)
41617      */
41618     select : function(index, scrollIntoView){
41619         this.selectedIndex = index;
41620         this.view.select(index);
41621         if(scrollIntoView !== false){
41622             var el = this.view.getNode(index);
41623             if(el){
41624                 this.innerList.scrollChildIntoView(el, false);
41625             }
41626         }
41627     },
41628
41629     // private
41630     selectNext : function(){
41631         var ct = this.store.getCount();
41632         if(ct > 0){
41633             if(this.selectedIndex == -1){
41634                 this.select(0);
41635             }else if(this.selectedIndex < ct-1){
41636                 this.select(this.selectedIndex+1);
41637             }
41638         }
41639     },
41640
41641     // private
41642     selectPrev : function(){
41643         var ct = this.store.getCount();
41644         if(ct > 0){
41645             if(this.selectedIndex == -1){
41646                 this.select(0);
41647             }else if(this.selectedIndex != 0){
41648                 this.select(this.selectedIndex-1);
41649             }
41650         }
41651     },
41652
41653     // private
41654     onKeyUp : function(e){
41655         if(this.editable !== false && !e.isSpecialKey()){
41656             this.lastKey = e.getKey();
41657             this.dqTask.delay(this.queryDelay);
41658         }
41659     },
41660
41661     // private
41662     validateBlur : function(){
41663         return !this.list || !this.list.isVisible();   
41664     },
41665
41666     // private
41667     initQuery : function(){
41668         this.doQuery(this.getRawValue());
41669     },
41670
41671     // private
41672     doForce : function(){
41673         if(this.el.dom.value.length > 0){
41674             this.el.dom.value =
41675                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41676              
41677         }
41678     },
41679
41680     /**
41681      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41682      * query allowing the query action to be canceled if needed.
41683      * @param {String} query The SQL query to execute
41684      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41685      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41686      * saved in the current store (defaults to false)
41687      */
41688     doQuery : function(q, forceAll){
41689         if(q === undefined || q === null){
41690             q = '';
41691         }
41692         var qe = {
41693             query: q,
41694             forceAll: forceAll,
41695             combo: this,
41696             cancel:false
41697         };
41698         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41699             return false;
41700         }
41701         q = qe.query;
41702         forceAll = qe.forceAll;
41703         if(forceAll === true || (q.length >= this.minChars)){
41704             if(this.lastQuery != q || this.alwaysQuery){
41705                 this.lastQuery = q;
41706                 if(this.mode == 'local'){
41707                     this.selectedIndex = -1;
41708                     if(forceAll){
41709                         this.store.clearFilter();
41710                     }else{
41711                         this.store.filter(this.displayField, q);
41712                     }
41713                     this.onLoad();
41714                 }else{
41715                     this.store.baseParams[this.queryParam] = q;
41716                     this.store.load({
41717                         params: this.getParams(q)
41718                     });
41719                     this.expand();
41720                 }
41721             }else{
41722                 this.selectedIndex = -1;
41723                 this.onLoad();   
41724             }
41725         }
41726     },
41727
41728     // private
41729     getParams : function(q){
41730         var p = {};
41731         //p[this.queryParam] = q;
41732         if(this.pageSize){
41733             p.start = 0;
41734             p.limit = this.pageSize;
41735         }
41736         return p;
41737     },
41738
41739     /**
41740      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41741      */
41742     collapse : function(){
41743         if(!this.isExpanded()){
41744             return;
41745         }
41746         this.list.hide();
41747         Roo.get(document).un('mousedown', this.collapseIf, this);
41748         Roo.get(document).un('mousewheel', this.collapseIf, this);
41749         if (!this.editable) {
41750             Roo.get(document).un('keydown', this.listKeyPress, this);
41751         }
41752         this.fireEvent('collapse', this);
41753     },
41754
41755     // private
41756     collapseIf : function(e){
41757         if(!e.within(this.wrap) && !e.within(this.list)){
41758             this.collapse();
41759         }
41760     },
41761
41762     /**
41763      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41764      */
41765     expand : function(){
41766         if(this.isExpanded() || !this.hasFocus){
41767             return;
41768         }
41769         this.list.alignTo(this.el, this.listAlign);
41770         this.list.show();
41771         Roo.get(document).on('mousedown', this.collapseIf, this);
41772         Roo.get(document).on('mousewheel', this.collapseIf, this);
41773         if (!this.editable) {
41774             Roo.get(document).on('keydown', this.listKeyPress, this);
41775         }
41776         
41777         this.fireEvent('expand', this);
41778     },
41779
41780     // private
41781     // Implements the default empty TriggerField.onTriggerClick function
41782     onTriggerClick : function(){
41783         if(this.disabled){
41784             return;
41785         }
41786         if(this.isExpanded()){
41787             this.collapse();
41788             if (!this.blockFocus) {
41789                 this.el.focus();
41790             }
41791             
41792         }else {
41793             this.hasFocus = true;
41794             if(this.triggerAction == 'all') {
41795                 this.doQuery(this.allQuery, true);
41796             } else {
41797                 this.doQuery(this.getRawValue());
41798             }
41799             if (!this.blockFocus) {
41800                 this.el.focus();
41801             }
41802         }
41803     },
41804     listKeyPress : function(e)
41805     {
41806         //Roo.log('listkeypress');
41807         // scroll to first matching element based on key pres..
41808         if (e.isSpecialKey()) {
41809             return false;
41810         }
41811         var k = String.fromCharCode(e.getKey()).toUpperCase();
41812         //Roo.log(k);
41813         var match  = false;
41814         var csel = this.view.getSelectedNodes();
41815         var cselitem = false;
41816         if (csel.length) {
41817             var ix = this.view.indexOf(csel[0]);
41818             cselitem  = this.store.getAt(ix);
41819             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41820                 cselitem = false;
41821             }
41822             
41823         }
41824         
41825         this.store.each(function(v) { 
41826             if (cselitem) {
41827                 // start at existing selection.
41828                 if (cselitem.id == v.id) {
41829                     cselitem = false;
41830                 }
41831                 return;
41832             }
41833                 
41834             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41835                 match = this.store.indexOf(v);
41836                 return false;
41837             }
41838         }, this);
41839         
41840         if (match === false) {
41841             return true; // no more action?
41842         }
41843         // scroll to?
41844         this.view.select(match);
41845         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41846         sn.scrollIntoView(sn.dom.parentNode, false);
41847     }
41848
41849     /** 
41850     * @cfg {Boolean} grow 
41851     * @hide 
41852     */
41853     /** 
41854     * @cfg {Number} growMin 
41855     * @hide 
41856     */
41857     /** 
41858     * @cfg {Number} growMax 
41859     * @hide 
41860     */
41861     /**
41862      * @hide
41863      * @method autoSize
41864      */
41865 });/*
41866  * Copyright(c) 2010-2012, Roo J Solutions Limited
41867  *
41868  * Licence LGPL
41869  *
41870  */
41871
41872 /**
41873  * @class Roo.form.ComboBoxArray
41874  * @extends Roo.form.TextField
41875  * A facebook style adder... for lists of email / people / countries  etc...
41876  * pick multiple items from a combo box, and shows each one.
41877  *
41878  *  Fred [x]  Brian [x]  [Pick another |v]
41879  *
41880  *
41881  *  For this to work: it needs various extra information
41882  *    - normal combo problay has
41883  *      name, hiddenName
41884  *    + displayField, valueField
41885  *
41886  *    For our purpose...
41887  *
41888  *
41889  *   If we change from 'extends' to wrapping...
41890  *   
41891  *  
41892  *
41893  
41894  
41895  * @constructor
41896  * Create a new ComboBoxArray.
41897  * @param {Object} config Configuration options
41898  */
41899  
41900
41901 Roo.form.ComboBoxArray = function(config)
41902 {
41903     this.addEvents({
41904         /**
41905          * @event beforeremove
41906          * Fires before remove the value from the list
41907              * @param {Roo.form.ComboBoxArray} _self This combo box array
41908              * @param {Roo.form.ComboBoxArray.Item} item removed item
41909              */
41910         'beforeremove' : true,
41911         /**
41912          * @event remove
41913          * Fires when remove the value from the list
41914              * @param {Roo.form.ComboBoxArray} _self This combo box array
41915              * @param {Roo.form.ComboBoxArray.Item} item removed item
41916              */
41917         'remove' : true
41918         
41919         
41920     });
41921     
41922     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41923     
41924     this.items = new Roo.util.MixedCollection(false);
41925     
41926     // construct the child combo...
41927     
41928     
41929     
41930     
41931    
41932     
41933 }
41934
41935  
41936 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41937
41938     /**
41939      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41940      */
41941     
41942     lastData : false,
41943     
41944     // behavies liek a hiddne field
41945     inputType:      'hidden',
41946     /**
41947      * @cfg {Number} width The width of the box that displays the selected element
41948      */ 
41949     width:          300,
41950
41951     
41952     
41953     /**
41954      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
41955      */
41956     name : false,
41957     /**
41958      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
41959      */
41960     hiddenName : false,
41961     
41962     
41963     // private the array of items that are displayed..
41964     items  : false,
41965     // private - the hidden field el.
41966     hiddenEl : false,
41967     // private - the filed el..
41968     el : false,
41969     
41970     //validateValue : function() { return true; }, // all values are ok!
41971     //onAddClick: function() { },
41972     
41973     onRender : function(ct, position) 
41974     {
41975         
41976         // create the standard hidden element
41977         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
41978         
41979         
41980         // give fake names to child combo;
41981         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
41982         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
41983         
41984         this.combo = Roo.factory(this.combo, Roo.form);
41985         this.combo.onRender(ct, position);
41986         if (typeof(this.combo.width) != 'undefined') {
41987             this.combo.onResize(this.combo.width,0);
41988         }
41989         
41990         this.combo.initEvents();
41991         
41992         // assigned so form know we need to do this..
41993         this.store          = this.combo.store;
41994         this.valueField     = this.combo.valueField;
41995         this.displayField   = this.combo.displayField ;
41996         
41997         
41998         this.combo.wrap.addClass('x-cbarray-grp');
41999         
42000         var cbwrap = this.combo.wrap.createChild(
42001             {tag: 'div', cls: 'x-cbarray-cb'},
42002             this.combo.el.dom
42003         );
42004         
42005              
42006         this.hiddenEl = this.combo.wrap.createChild({
42007             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42008         });
42009         this.el = this.combo.wrap.createChild({
42010             tag: 'input',  type:'hidden' , name: this.name, value : ''
42011         });
42012          //   this.el.dom.removeAttribute("name");
42013         
42014         
42015         this.outerWrap = this.combo.wrap;
42016         this.wrap = cbwrap;
42017         
42018         this.outerWrap.setWidth(this.width);
42019         this.outerWrap.dom.removeChild(this.el.dom);
42020         
42021         this.wrap.dom.appendChild(this.el.dom);
42022         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42023         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42024         
42025         this.combo.trigger.setStyle('position','relative');
42026         this.combo.trigger.setStyle('left', '0px');
42027         this.combo.trigger.setStyle('top', '2px');
42028         
42029         this.combo.el.setStyle('vertical-align', 'text-bottom');
42030         
42031         //this.trigger.setStyle('vertical-align', 'top');
42032         
42033         // this should use the code from combo really... on('add' ....)
42034         if (this.adder) {
42035             
42036         
42037             this.adder = this.outerWrap.createChild(
42038                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42039             var _t = this;
42040             this.adder.on('click', function(e) {
42041                 _t.fireEvent('adderclick', this, e);
42042             }, _t);
42043         }
42044         //var _t = this;
42045         //this.adder.on('click', this.onAddClick, _t);
42046         
42047         
42048         this.combo.on('select', function(cb, rec, ix) {
42049             this.addItem(rec.data);
42050             
42051             cb.setValue('');
42052             cb.el.dom.value = '';
42053             //cb.lastData = rec.data;
42054             // add to list
42055             
42056         }, this);
42057         
42058         
42059     },
42060     
42061     
42062     getName: function()
42063     {
42064         // returns hidden if it's set..
42065         if (!this.rendered) {return ''};
42066         return  this.hiddenName ? this.hiddenName : this.name;
42067         
42068     },
42069     
42070     
42071     onResize: function(w, h){
42072         
42073         return;
42074         // not sure if this is needed..
42075         //this.combo.onResize(w,h);
42076         
42077         if(typeof w != 'number'){
42078             // we do not handle it!?!?
42079             return;
42080         }
42081         var tw = this.combo.trigger.getWidth();
42082         tw += this.addicon ? this.addicon.getWidth() : 0;
42083         tw += this.editicon ? this.editicon.getWidth() : 0;
42084         var x = w - tw;
42085         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42086             
42087         this.combo.trigger.setStyle('left', '0px');
42088         
42089         if(this.list && this.listWidth === undefined){
42090             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42091             this.list.setWidth(lw);
42092             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42093         }
42094         
42095     
42096         
42097     },
42098     
42099     addItem: function(rec)
42100     {
42101         var valueField = this.combo.valueField;
42102         var displayField = this.combo.displayField;
42103         if (this.items.indexOfKey(rec[valueField]) > -1) {
42104             //console.log("GOT " + rec.data.id);
42105             return;
42106         }
42107         
42108         var x = new Roo.form.ComboBoxArray.Item({
42109             //id : rec[this.idField],
42110             data : rec,
42111             displayField : displayField ,
42112             tipField : displayField ,
42113             cb : this
42114         });
42115         // use the 
42116         this.items.add(rec[valueField],x);
42117         // add it before the element..
42118         this.updateHiddenEl();
42119         x.render(this.outerWrap, this.wrap.dom);
42120         // add the image handler..
42121     },
42122     
42123     updateHiddenEl : function()
42124     {
42125         this.validate();
42126         if (!this.hiddenEl) {
42127             return;
42128         }
42129         var ar = [];
42130         var idField = this.combo.valueField;
42131         
42132         this.items.each(function(f) {
42133             ar.push(f.data[idField]);
42134            
42135         });
42136         this.hiddenEl.dom.value = ar.join(',');
42137         this.validate();
42138     },
42139     
42140     reset : function()
42141     {
42142         this.items.clear();
42143         
42144         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42145            el.remove();
42146         });
42147         
42148         this.el.dom.value = '';
42149         if (this.hiddenEl) {
42150             this.hiddenEl.dom.value = '';
42151         }
42152         
42153     },
42154     getValue: function()
42155     {
42156         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42157     },
42158     setValue: function(v) // not a valid action - must use addItems..
42159     {
42160          
42161         this.reset();
42162         
42163         
42164         
42165         if (this.store.isLocal && (typeof(v) == 'string')) {
42166             // then we can use the store to find the values..
42167             // comma seperated at present.. this needs to allow JSON based encoding..
42168             this.hiddenEl.value  = v;
42169             var v_ar = [];
42170             Roo.each(v.split(','), function(k) {
42171                 Roo.log("CHECK " + this.valueField + ',' + k);
42172                 var li = this.store.query(this.valueField, k);
42173                 if (!li.length) {
42174                     return;
42175                 }
42176                 var add = {};
42177                 add[this.valueField] = k;
42178                 add[this.displayField] = li.item(0).data[this.displayField];
42179                 
42180                 this.addItem(add);
42181             }, this) 
42182              
42183         }
42184         if (typeof(v) == 'object' ) {
42185             // then let's assume it's an array of objects..
42186             Roo.each(v, function(l) {
42187                 this.addItem(l);
42188             }, this);
42189              
42190         }
42191         
42192         
42193     },
42194     setFromData: function(v)
42195     {
42196         // this recieves an object, if setValues is called.
42197         this.reset();
42198         this.el.dom.value = v[this.displayField];
42199         this.hiddenEl.dom.value = v[this.valueField];
42200         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42201             return;
42202         }
42203         var kv = v[this.valueField];
42204         var dv = v[this.displayField];
42205         kv = typeof(kv) != 'string' ? '' : kv;
42206         dv = typeof(dv) != 'string' ? '' : dv;
42207         
42208         
42209         var keys = kv.split(',');
42210         var display = dv.split(',');
42211         for (var i = 0 ; i < keys.length; i++) {
42212             
42213             add = {};
42214             add[this.valueField] = keys[i];
42215             add[this.displayField] = display[i];
42216             this.addItem(add);
42217         }
42218       
42219         
42220     },
42221     
42222     /**
42223      * Validates the combox array value
42224      * @return {Boolean} True if the value is valid, else false
42225      */
42226     validate : function(){
42227         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42228             this.clearInvalid();
42229             return true;
42230         }
42231         return false;
42232     },
42233     
42234     validateValue : function(value){
42235         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42236         
42237     },
42238     
42239     /*@
42240      * overide
42241      * 
42242      */
42243     isDirty : function() {
42244         if(this.disabled) {
42245             return false;
42246         }
42247         
42248         try {
42249             var d = Roo.decode(String(this.originalValue));
42250         } catch (e) {
42251             return String(this.getValue()) !== String(this.originalValue);
42252         }
42253         
42254         var originalValue = [];
42255         
42256         for (var i = 0; i < d.length; i++){
42257             originalValue.push(d[i][this.valueField]);
42258         }
42259         
42260         return String(this.getValue()) !== String(originalValue.join(','));
42261         
42262     }
42263     
42264 });
42265
42266
42267
42268 /**
42269  * @class Roo.form.ComboBoxArray.Item
42270  * @extends Roo.BoxComponent
42271  * A selected item in the list
42272  *  Fred [x]  Brian [x]  [Pick another |v]
42273  * 
42274  * @constructor
42275  * Create a new item.
42276  * @param {Object} config Configuration options
42277  */
42278  
42279 Roo.form.ComboBoxArray.Item = function(config) {
42280     config.id = Roo.id();
42281     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42282 }
42283
42284 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42285     data : {},
42286     cb: false,
42287     displayField : false,
42288     tipField : false,
42289     
42290     
42291     defaultAutoCreate : {
42292         tag: 'div',
42293         cls: 'x-cbarray-item',
42294         cn : [ 
42295             { tag: 'div' },
42296             {
42297                 tag: 'img',
42298                 width:16,
42299                 height : 16,
42300                 src : Roo.BLANK_IMAGE_URL ,
42301                 align: 'center'
42302             }
42303         ]
42304         
42305     },
42306     
42307  
42308     onRender : function(ct, position)
42309     {
42310         Roo.form.Field.superclass.onRender.call(this, ct, position);
42311         
42312         if(!this.el){
42313             var cfg = this.getAutoCreate();
42314             this.el = ct.createChild(cfg, position);
42315         }
42316         
42317         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42318         
42319         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42320             this.cb.renderer(this.data) :
42321             String.format('{0}',this.data[this.displayField]);
42322         
42323             
42324         this.el.child('div').dom.setAttribute('qtip',
42325                         String.format('{0}',this.data[this.tipField])
42326         );
42327         
42328         this.el.child('img').on('click', this.remove, this);
42329         
42330     },
42331    
42332     remove : function()
42333     {
42334         if(this.cb.disabled){
42335             return;
42336         }
42337         
42338         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42339             this.cb.items.remove(this);
42340             this.el.child('img').un('click', this.remove, this);
42341             this.el.remove();
42342             this.cb.updateHiddenEl();
42343
42344             this.cb.fireEvent('remove', this.cb, this);
42345         }
42346         
42347     }
42348 });/*
42349  * Based on:
42350  * Ext JS Library 1.1.1
42351  * Copyright(c) 2006-2007, Ext JS, LLC.
42352  *
42353  * Originally Released Under LGPL - original licence link has changed is not relivant.
42354  *
42355  * Fork - LGPL
42356  * <script type="text/javascript">
42357  */
42358 /**
42359  * @class Roo.form.Checkbox
42360  * @extends Roo.form.Field
42361  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42362  * @constructor
42363  * Creates a new Checkbox
42364  * @param {Object} config Configuration options
42365  */
42366 Roo.form.Checkbox = function(config){
42367     Roo.form.Checkbox.superclass.constructor.call(this, config);
42368     this.addEvents({
42369         /**
42370          * @event check
42371          * Fires when the checkbox is checked or unchecked.
42372              * @param {Roo.form.Checkbox} this This checkbox
42373              * @param {Boolean} checked The new checked value
42374              */
42375         check : true
42376     });
42377 };
42378
42379 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42380     /**
42381      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42382      */
42383     focusClass : undefined,
42384     /**
42385      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42386      */
42387     fieldClass: "x-form-field",
42388     /**
42389      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42390      */
42391     checked: false,
42392     /**
42393      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42394      * {tag: "input", type: "checkbox", autocomplete: "off"})
42395      */
42396     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42397     /**
42398      * @cfg {String} boxLabel The text that appears beside the checkbox
42399      */
42400     boxLabel : "",
42401     /**
42402      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42403      */  
42404     inputValue : '1',
42405     /**
42406      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42407      */
42408      valueOff: '0', // value when not checked..
42409
42410     actionMode : 'viewEl', 
42411     //
42412     // private
42413     itemCls : 'x-menu-check-item x-form-item',
42414     groupClass : 'x-menu-group-item',
42415     inputType : 'hidden',
42416     
42417     
42418     inSetChecked: false, // check that we are not calling self...
42419     
42420     inputElement: false, // real input element?
42421     basedOn: false, // ????
42422     
42423     isFormField: true, // not sure where this is needed!!!!
42424
42425     onResize : function(){
42426         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42427         if(!this.boxLabel){
42428             this.el.alignTo(this.wrap, 'c-c');
42429         }
42430     },
42431
42432     initEvents : function(){
42433         Roo.form.Checkbox.superclass.initEvents.call(this);
42434         this.el.on("click", this.onClick,  this);
42435         this.el.on("change", this.onClick,  this);
42436     },
42437
42438
42439     getResizeEl : function(){
42440         return this.wrap;
42441     },
42442
42443     getPositionEl : function(){
42444         return this.wrap;
42445     },
42446
42447     // private
42448     onRender : function(ct, position){
42449         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42450         /*
42451         if(this.inputValue !== undefined){
42452             this.el.dom.value = this.inputValue;
42453         }
42454         */
42455         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42456         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42457         var viewEl = this.wrap.createChild({ 
42458             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42459         this.viewEl = viewEl;   
42460         this.wrap.on('click', this.onClick,  this); 
42461         
42462         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42463         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42464         
42465         
42466         
42467         if(this.boxLabel){
42468             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42469         //    viewEl.on('click', this.onClick,  this); 
42470         }
42471         //if(this.checked){
42472             this.setChecked(this.checked);
42473         //}else{
42474             //this.checked = this.el.dom;
42475         //}
42476
42477     },
42478
42479     // private
42480     initValue : Roo.emptyFn,
42481
42482     /**
42483      * Returns the checked state of the checkbox.
42484      * @return {Boolean} True if checked, else false
42485      */
42486     getValue : function(){
42487         if(this.el){
42488             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42489         }
42490         return this.valueOff;
42491         
42492     },
42493
42494         // private
42495     onClick : function(){ 
42496         if (this.disabled) {
42497             return;
42498         }
42499         this.setChecked(!this.checked);
42500
42501         //if(this.el.dom.checked != this.checked){
42502         //    this.setValue(this.el.dom.checked);
42503        // }
42504     },
42505
42506     /**
42507      * Sets the checked state of the checkbox.
42508      * On is always based on a string comparison between inputValue and the param.
42509      * @param {Boolean/String} value - the value to set 
42510      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42511      */
42512     setValue : function(v,suppressEvent){
42513         
42514         
42515         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42516         //if(this.el && this.el.dom){
42517         //    this.el.dom.checked = this.checked;
42518         //    this.el.dom.defaultChecked = this.checked;
42519         //}
42520         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42521         //this.fireEvent("check", this, this.checked);
42522     },
42523     // private..
42524     setChecked : function(state,suppressEvent)
42525     {
42526         if (this.inSetChecked) {
42527             this.checked = state;
42528             return;
42529         }
42530         
42531     
42532         if(this.wrap){
42533             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42534         }
42535         this.checked = state;
42536         if(suppressEvent !== true){
42537             this.fireEvent('check', this, state);
42538         }
42539         this.inSetChecked = true;
42540         this.el.dom.value = state ? this.inputValue : this.valueOff;
42541         this.inSetChecked = false;
42542         
42543     },
42544     // handle setting of hidden value by some other method!!?!?
42545     setFromHidden: function()
42546     {
42547         if(!this.el){
42548             return;
42549         }
42550         //console.log("SET FROM HIDDEN");
42551         //alert('setFrom hidden');
42552         this.setValue(this.el.dom.value);
42553     },
42554     
42555     onDestroy : function()
42556     {
42557         if(this.viewEl){
42558             Roo.get(this.viewEl).remove();
42559         }
42560          
42561         Roo.form.Checkbox.superclass.onDestroy.call(this);
42562     },
42563     
42564     setBoxLabel : function(str)
42565     {
42566         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42567     }
42568
42569 });/*
42570  * Based on:
42571  * Ext JS Library 1.1.1
42572  * Copyright(c) 2006-2007, Ext JS, LLC.
42573  *
42574  * Originally Released Under LGPL - original licence link has changed is not relivant.
42575  *
42576  * Fork - LGPL
42577  * <script type="text/javascript">
42578  */
42579  
42580 /**
42581  * @class Roo.form.Radio
42582  * @extends Roo.form.Checkbox
42583  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42584  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42585  * @constructor
42586  * Creates a new Radio
42587  * @param {Object} config Configuration options
42588  */
42589 Roo.form.Radio = function(){
42590     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42591 };
42592 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42593     inputType: 'radio',
42594
42595     /**
42596      * If this radio is part of a group, it will return the selected value
42597      * @return {String}
42598      */
42599     getGroupValue : function(){
42600         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42601     },
42602     
42603     
42604     onRender : function(ct, position){
42605         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42606         
42607         if(this.inputValue !== undefined){
42608             this.el.dom.value = this.inputValue;
42609         }
42610          
42611         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42612         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42613         //var viewEl = this.wrap.createChild({ 
42614         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42615         //this.viewEl = viewEl;   
42616         //this.wrap.on('click', this.onClick,  this); 
42617         
42618         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42619         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42620         
42621         
42622         
42623         if(this.boxLabel){
42624             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42625         //    viewEl.on('click', this.onClick,  this); 
42626         }
42627          if(this.checked){
42628             this.el.dom.checked =   'checked' ;
42629         }
42630          
42631     } 
42632     
42633     
42634 });//<script type="text/javascript">
42635
42636 /*
42637  * Based  Ext JS Library 1.1.1
42638  * Copyright(c) 2006-2007, Ext JS, LLC.
42639  * LGPL
42640  *
42641  */
42642  
42643 /**
42644  * @class Roo.HtmlEditorCore
42645  * @extends Roo.Component
42646  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42647  *
42648  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42649  */
42650
42651 Roo.HtmlEditorCore = function(config){
42652     
42653     
42654     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42655     
42656     
42657     this.addEvents({
42658         /**
42659          * @event initialize
42660          * Fires when the editor is fully initialized (including the iframe)
42661          * @param {Roo.HtmlEditorCore} this
42662          */
42663         initialize: true,
42664         /**
42665          * @event activate
42666          * Fires when the editor is first receives the focus. Any insertion must wait
42667          * until after this event.
42668          * @param {Roo.HtmlEditorCore} this
42669          */
42670         activate: true,
42671          /**
42672          * @event beforesync
42673          * Fires before the textarea is updated with content from the editor iframe. Return false
42674          * to cancel the sync.
42675          * @param {Roo.HtmlEditorCore} this
42676          * @param {String} html
42677          */
42678         beforesync: true,
42679          /**
42680          * @event beforepush
42681          * Fires before the iframe editor is updated with content from the textarea. Return false
42682          * to cancel the push.
42683          * @param {Roo.HtmlEditorCore} this
42684          * @param {String} html
42685          */
42686         beforepush: true,
42687          /**
42688          * @event sync
42689          * Fires when the textarea is updated with content from the editor iframe.
42690          * @param {Roo.HtmlEditorCore} this
42691          * @param {String} html
42692          */
42693         sync: true,
42694          /**
42695          * @event push
42696          * Fires when the iframe editor is updated with content from the textarea.
42697          * @param {Roo.HtmlEditorCore} this
42698          * @param {String} html
42699          */
42700         push: true,
42701         
42702         /**
42703          * @event editorevent
42704          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42705          * @param {Roo.HtmlEditorCore} this
42706          */
42707         editorevent: true
42708         
42709     });
42710     
42711     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42712     
42713     // defaults : white / black...
42714     this.applyBlacklists();
42715     
42716     
42717     
42718 };
42719
42720
42721 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42722
42723
42724      /**
42725      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42726      */
42727     
42728     owner : false,
42729     
42730      /**
42731      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42732      *                        Roo.resizable.
42733      */
42734     resizable : false,
42735      /**
42736      * @cfg {Number} height (in pixels)
42737      */   
42738     height: 300,
42739    /**
42740      * @cfg {Number} width (in pixels)
42741      */   
42742     width: 500,
42743     
42744     /**
42745      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42746      * 
42747      */
42748     stylesheets: false,
42749     
42750     // id of frame..
42751     frameId: false,
42752     
42753     // private properties
42754     validationEvent : false,
42755     deferHeight: true,
42756     initialized : false,
42757     activated : false,
42758     sourceEditMode : false,
42759     onFocus : Roo.emptyFn,
42760     iframePad:3,
42761     hideMode:'offsets',
42762     
42763     clearUp: true,
42764     
42765     // blacklist + whitelisted elements..
42766     black: false,
42767     white: false,
42768      
42769     
42770
42771     /**
42772      * Protected method that will not generally be called directly. It
42773      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42774      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42775      */
42776     getDocMarkup : function(){
42777         // body styles..
42778         var st = '';
42779         
42780         // inherit styels from page...?? 
42781         if (this.stylesheets === false) {
42782             
42783             Roo.get(document.head).select('style').each(function(node) {
42784                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42785             });
42786             
42787             Roo.get(document.head).select('link').each(function(node) { 
42788                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42789             });
42790             
42791         } else if (!this.stylesheets.length) {
42792                 // simple..
42793                 st = '<style type="text/css">' +
42794                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42795                    '</style>';
42796         } else { 
42797             
42798         }
42799         
42800         st +=  '<style type="text/css">' +
42801             'IMG { cursor: pointer } ' +
42802         '</style>';
42803
42804         
42805         return '<html><head>' + st  +
42806             //<style type="text/css">' +
42807             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42808             //'</style>' +
42809             ' </head><body class="roo-htmleditor-body"></body></html>';
42810     },
42811
42812     // private
42813     onRender : function(ct, position)
42814     {
42815         var _t = this;
42816         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42817         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42818         
42819         
42820         this.el.dom.style.border = '0 none';
42821         this.el.dom.setAttribute('tabIndex', -1);
42822         this.el.addClass('x-hidden hide');
42823         
42824         
42825         
42826         if(Roo.isIE){ // fix IE 1px bogus margin
42827             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42828         }
42829        
42830         
42831         this.frameId = Roo.id();
42832         
42833          
42834         
42835         var iframe = this.owner.wrap.createChild({
42836             tag: 'iframe',
42837             cls: 'form-control', // bootstrap..
42838             id: this.frameId,
42839             name: this.frameId,
42840             frameBorder : 'no',
42841             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42842         }, this.el
42843         );
42844         
42845         
42846         this.iframe = iframe.dom;
42847
42848          this.assignDocWin();
42849         
42850         this.doc.designMode = 'on';
42851        
42852         this.doc.open();
42853         this.doc.write(this.getDocMarkup());
42854         this.doc.close();
42855
42856         
42857         var task = { // must defer to wait for browser to be ready
42858             run : function(){
42859                 //console.log("run task?" + this.doc.readyState);
42860                 this.assignDocWin();
42861                 if(this.doc.body || this.doc.readyState == 'complete'){
42862                     try {
42863                         this.doc.designMode="on";
42864                     } catch (e) {
42865                         return;
42866                     }
42867                     Roo.TaskMgr.stop(task);
42868                     this.initEditor.defer(10, this);
42869                 }
42870             },
42871             interval : 10,
42872             duration: 10000,
42873             scope: this
42874         };
42875         Roo.TaskMgr.start(task);
42876
42877     },
42878
42879     // private
42880     onResize : function(w, h)
42881     {
42882          Roo.log('resize: ' +w + ',' + h );
42883         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42884         if(!this.iframe){
42885             return;
42886         }
42887         if(typeof w == 'number'){
42888             
42889             this.iframe.style.width = w + 'px';
42890         }
42891         if(typeof h == 'number'){
42892             
42893             this.iframe.style.height = h + 'px';
42894             if(this.doc){
42895                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42896             }
42897         }
42898         
42899     },
42900
42901     /**
42902      * Toggles the editor between standard and source edit mode.
42903      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42904      */
42905     toggleSourceEdit : function(sourceEditMode){
42906         
42907         this.sourceEditMode = sourceEditMode === true;
42908         
42909         if(this.sourceEditMode){
42910  
42911             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42912             
42913         }else{
42914             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42915             //this.iframe.className = '';
42916             this.deferFocus();
42917         }
42918         //this.setSize(this.owner.wrap.getSize());
42919         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42920     },
42921
42922     
42923   
42924
42925     /**
42926      * Protected method that will not generally be called directly. If you need/want
42927      * custom HTML cleanup, this is the method you should override.
42928      * @param {String} html The HTML to be cleaned
42929      * return {String} The cleaned HTML
42930      */
42931     cleanHtml : function(html){
42932         html = String(html);
42933         if(html.length > 5){
42934             if(Roo.isSafari){ // strip safari nonsense
42935                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42936             }
42937         }
42938         if(html == '&nbsp;'){
42939             html = '';
42940         }
42941         return html;
42942     },
42943
42944     /**
42945      * HTML Editor -> Textarea
42946      * Protected method that will not generally be called directly. Syncs the contents
42947      * of the editor iframe with the textarea.
42948      */
42949     syncValue : function(){
42950         if(this.initialized){
42951             var bd = (this.doc.body || this.doc.documentElement);
42952             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42953             var html = bd.innerHTML;
42954             if(Roo.isSafari){
42955                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
42956                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
42957                 if(m && m[1]){
42958                     html = '<div style="'+m[0]+'">' + html + '</div>';
42959                 }
42960             }
42961             html = this.cleanHtml(html);
42962             // fix up the special chars.. normaly like back quotes in word...
42963             // however we do not want to do this with chinese..
42964             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
42965                 var cc = b.charCodeAt();
42966                 if (
42967                     (cc >= 0x4E00 && cc < 0xA000 ) ||
42968                     (cc >= 0x3400 && cc < 0x4E00 ) ||
42969                     (cc >= 0xf900 && cc < 0xfb00 )
42970                 ) {
42971                         return b;
42972                 }
42973                 return "&#"+cc+";" 
42974             });
42975             if(this.owner.fireEvent('beforesync', this, html) !== false){
42976                 this.el.dom.value = html;
42977                 this.owner.fireEvent('sync', this, html);
42978             }
42979         }
42980     },
42981
42982     /**
42983      * Protected method that will not generally be called directly. Pushes the value of the textarea
42984      * into the iframe editor.
42985      */
42986     pushValue : function(){
42987         if(this.initialized){
42988             var v = this.el.dom.value.trim();
42989             
42990 //            if(v.length < 1){
42991 //                v = '&#160;';
42992 //            }
42993             
42994             if(this.owner.fireEvent('beforepush', this, v) !== false){
42995                 var d = (this.doc.body || this.doc.documentElement);
42996                 d.innerHTML = v;
42997                 this.cleanUpPaste();
42998                 this.el.dom.value = d.innerHTML;
42999                 this.owner.fireEvent('push', this, v);
43000             }
43001         }
43002     },
43003
43004     // private
43005     deferFocus : function(){
43006         this.focus.defer(10, this);
43007     },
43008
43009     // doc'ed in Field
43010     focus : function(){
43011         if(this.win && !this.sourceEditMode){
43012             this.win.focus();
43013         }else{
43014             this.el.focus();
43015         }
43016     },
43017     
43018     assignDocWin: function()
43019     {
43020         var iframe = this.iframe;
43021         
43022          if(Roo.isIE){
43023             this.doc = iframe.contentWindow.document;
43024             this.win = iframe.contentWindow;
43025         } else {
43026 //            if (!Roo.get(this.frameId)) {
43027 //                return;
43028 //            }
43029 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43030 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43031             
43032             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43033                 return;
43034             }
43035             
43036             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43037             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43038         }
43039     },
43040     
43041     // private
43042     initEditor : function(){
43043         //console.log("INIT EDITOR");
43044         this.assignDocWin();
43045         
43046         
43047         
43048         this.doc.designMode="on";
43049         this.doc.open();
43050         this.doc.write(this.getDocMarkup());
43051         this.doc.close();
43052         
43053         var dbody = (this.doc.body || this.doc.documentElement);
43054         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43055         // this copies styles from the containing element into thsi one..
43056         // not sure why we need all of this..
43057         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43058         
43059         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43060         //ss['background-attachment'] = 'fixed'; // w3c
43061         dbody.bgProperties = 'fixed'; // ie
43062         //Roo.DomHelper.applyStyles(dbody, ss);
43063         Roo.EventManager.on(this.doc, {
43064             //'mousedown': this.onEditorEvent,
43065             'mouseup': this.onEditorEvent,
43066             'dblclick': this.onEditorEvent,
43067             'click': this.onEditorEvent,
43068             'keyup': this.onEditorEvent,
43069             buffer:100,
43070             scope: this
43071         });
43072         if(Roo.isGecko){
43073             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43074         }
43075         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43076             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43077         }
43078         this.initialized = true;
43079
43080         this.owner.fireEvent('initialize', this);
43081         this.pushValue();
43082     },
43083
43084     // private
43085     onDestroy : function(){
43086         
43087         
43088         
43089         if(this.rendered){
43090             
43091             //for (var i =0; i < this.toolbars.length;i++) {
43092             //    // fixme - ask toolbars for heights?
43093             //    this.toolbars[i].onDestroy();
43094            // }
43095             
43096             //this.wrap.dom.innerHTML = '';
43097             //this.wrap.remove();
43098         }
43099     },
43100
43101     // private
43102     onFirstFocus : function(){
43103         
43104         this.assignDocWin();
43105         
43106         
43107         this.activated = true;
43108          
43109     
43110         if(Roo.isGecko){ // prevent silly gecko errors
43111             this.win.focus();
43112             var s = this.win.getSelection();
43113             if(!s.focusNode || s.focusNode.nodeType != 3){
43114                 var r = s.getRangeAt(0);
43115                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43116                 r.collapse(true);
43117                 this.deferFocus();
43118             }
43119             try{
43120                 this.execCmd('useCSS', true);
43121                 this.execCmd('styleWithCSS', false);
43122             }catch(e){}
43123         }
43124         this.owner.fireEvent('activate', this);
43125     },
43126
43127     // private
43128     adjustFont: function(btn){
43129         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43130         //if(Roo.isSafari){ // safari
43131         //    adjust *= 2;
43132        // }
43133         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43134         if(Roo.isSafari){ // safari
43135             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43136             v =  (v < 10) ? 10 : v;
43137             v =  (v > 48) ? 48 : v;
43138             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43139             
43140         }
43141         
43142         
43143         v = Math.max(1, v+adjust);
43144         
43145         this.execCmd('FontSize', v  );
43146     },
43147
43148     onEditorEvent : function(e)
43149     {
43150         this.owner.fireEvent('editorevent', this, e);
43151       //  this.updateToolbar();
43152         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43153     },
43154
43155     insertTag : function(tg)
43156     {
43157         // could be a bit smarter... -> wrap the current selected tRoo..
43158         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43159             
43160             range = this.createRange(this.getSelection());
43161             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43162             wrappingNode.appendChild(range.extractContents());
43163             range.insertNode(wrappingNode);
43164
43165             return;
43166             
43167             
43168             
43169         }
43170         this.execCmd("formatblock",   tg);
43171         
43172     },
43173     
43174     insertText : function(txt)
43175     {
43176         
43177         
43178         var range = this.createRange();
43179         range.deleteContents();
43180                //alert(Sender.getAttribute('label'));
43181                
43182         range.insertNode(this.doc.createTextNode(txt));
43183     } ,
43184     
43185      
43186
43187     /**
43188      * Executes a Midas editor command on the editor document and performs necessary focus and
43189      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43190      * @param {String} cmd The Midas command
43191      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43192      */
43193     relayCmd : function(cmd, value){
43194         this.win.focus();
43195         this.execCmd(cmd, value);
43196         this.owner.fireEvent('editorevent', this);
43197         //this.updateToolbar();
43198         this.owner.deferFocus();
43199     },
43200
43201     /**
43202      * Executes a Midas editor command directly on the editor document.
43203      * For visual commands, you should use {@link #relayCmd} instead.
43204      * <b>This should only be called after the editor is initialized.</b>
43205      * @param {String} cmd The Midas command
43206      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43207      */
43208     execCmd : function(cmd, value){
43209         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43210         this.syncValue();
43211     },
43212  
43213  
43214    
43215     /**
43216      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43217      * to insert tRoo.
43218      * @param {String} text | dom node.. 
43219      */
43220     insertAtCursor : function(text)
43221     {
43222         
43223         
43224         
43225         if(!this.activated){
43226             return;
43227         }
43228         /*
43229         if(Roo.isIE){
43230             this.win.focus();
43231             var r = this.doc.selection.createRange();
43232             if(r){
43233                 r.collapse(true);
43234                 r.pasteHTML(text);
43235                 this.syncValue();
43236                 this.deferFocus();
43237             
43238             }
43239             return;
43240         }
43241         */
43242         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43243             this.win.focus();
43244             
43245             
43246             // from jquery ui (MIT licenced)
43247             var range, node;
43248             var win = this.win;
43249             
43250             if (win.getSelection && win.getSelection().getRangeAt) {
43251                 range = win.getSelection().getRangeAt(0);
43252                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43253                 range.insertNode(node);
43254             } else if (win.document.selection && win.document.selection.createRange) {
43255                 // no firefox support
43256                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43257                 win.document.selection.createRange().pasteHTML(txt);
43258             } else {
43259                 // no firefox support
43260                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43261                 this.execCmd('InsertHTML', txt);
43262             } 
43263             
43264             this.syncValue();
43265             
43266             this.deferFocus();
43267         }
43268     },
43269  // private
43270     mozKeyPress : function(e){
43271         if(e.ctrlKey){
43272             var c = e.getCharCode(), cmd;
43273           
43274             if(c > 0){
43275                 c = String.fromCharCode(c).toLowerCase();
43276                 switch(c){
43277                     case 'b':
43278                         cmd = 'bold';
43279                         break;
43280                     case 'i':
43281                         cmd = 'italic';
43282                         break;
43283                     
43284                     case 'u':
43285                         cmd = 'underline';
43286                         break;
43287                     
43288                     case 'v':
43289                         this.cleanUpPaste.defer(100, this);
43290                         return;
43291                         
43292                 }
43293                 if(cmd){
43294                     this.win.focus();
43295                     this.execCmd(cmd);
43296                     this.deferFocus();
43297                     e.preventDefault();
43298                 }
43299                 
43300             }
43301         }
43302     },
43303
43304     // private
43305     fixKeys : function(){ // load time branching for fastest keydown performance
43306         if(Roo.isIE){
43307             return function(e){
43308                 var k = e.getKey(), r;
43309                 if(k == e.TAB){
43310                     e.stopEvent();
43311                     r = this.doc.selection.createRange();
43312                     if(r){
43313                         r.collapse(true);
43314                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43315                         this.deferFocus();
43316                     }
43317                     return;
43318                 }
43319                 
43320                 if(k == e.ENTER){
43321                     r = this.doc.selection.createRange();
43322                     if(r){
43323                         var target = r.parentElement();
43324                         if(!target || target.tagName.toLowerCase() != 'li'){
43325                             e.stopEvent();
43326                             r.pasteHTML('<br />');
43327                             r.collapse(false);
43328                             r.select();
43329                         }
43330                     }
43331                 }
43332                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43333                     this.cleanUpPaste.defer(100, this);
43334                     return;
43335                 }
43336                 
43337                 
43338             };
43339         }else if(Roo.isOpera){
43340             return function(e){
43341                 var k = e.getKey();
43342                 if(k == e.TAB){
43343                     e.stopEvent();
43344                     this.win.focus();
43345                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43346                     this.deferFocus();
43347                 }
43348                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43349                     this.cleanUpPaste.defer(100, this);
43350                     return;
43351                 }
43352                 
43353             };
43354         }else if(Roo.isSafari){
43355             return function(e){
43356                 var k = e.getKey();
43357                 
43358                 if(k == e.TAB){
43359                     e.stopEvent();
43360                     this.execCmd('InsertText','\t');
43361                     this.deferFocus();
43362                     return;
43363                 }
43364                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43365                     this.cleanUpPaste.defer(100, this);
43366                     return;
43367                 }
43368                 
43369              };
43370         }
43371     }(),
43372     
43373     getAllAncestors: function()
43374     {
43375         var p = this.getSelectedNode();
43376         var a = [];
43377         if (!p) {
43378             a.push(p); // push blank onto stack..
43379             p = this.getParentElement();
43380         }
43381         
43382         
43383         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43384             a.push(p);
43385             p = p.parentNode;
43386         }
43387         a.push(this.doc.body);
43388         return a;
43389     },
43390     lastSel : false,
43391     lastSelNode : false,
43392     
43393     
43394     getSelection : function() 
43395     {
43396         this.assignDocWin();
43397         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43398     },
43399     
43400     getSelectedNode: function() 
43401     {
43402         // this may only work on Gecko!!!
43403         
43404         // should we cache this!!!!
43405         
43406         
43407         
43408          
43409         var range = this.createRange(this.getSelection()).cloneRange();
43410         
43411         if (Roo.isIE) {
43412             var parent = range.parentElement();
43413             while (true) {
43414                 var testRange = range.duplicate();
43415                 testRange.moveToElementText(parent);
43416                 if (testRange.inRange(range)) {
43417                     break;
43418                 }
43419                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43420                     break;
43421                 }
43422                 parent = parent.parentElement;
43423             }
43424             return parent;
43425         }
43426         
43427         // is ancestor a text element.
43428         var ac =  range.commonAncestorContainer;
43429         if (ac.nodeType == 3) {
43430             ac = ac.parentNode;
43431         }
43432         
43433         var ar = ac.childNodes;
43434          
43435         var nodes = [];
43436         var other_nodes = [];
43437         var has_other_nodes = false;
43438         for (var i=0;i<ar.length;i++) {
43439             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43440                 continue;
43441             }
43442             // fullly contained node.
43443             
43444             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43445                 nodes.push(ar[i]);
43446                 continue;
43447             }
43448             
43449             // probably selected..
43450             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43451                 other_nodes.push(ar[i]);
43452                 continue;
43453             }
43454             // outer..
43455             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43456                 continue;
43457             }
43458             
43459             
43460             has_other_nodes = true;
43461         }
43462         if (!nodes.length && other_nodes.length) {
43463             nodes= other_nodes;
43464         }
43465         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43466             return false;
43467         }
43468         
43469         return nodes[0];
43470     },
43471     createRange: function(sel)
43472     {
43473         // this has strange effects when using with 
43474         // top toolbar - not sure if it's a great idea.
43475         //this.editor.contentWindow.focus();
43476         if (typeof sel != "undefined") {
43477             try {
43478                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43479             } catch(e) {
43480                 return this.doc.createRange();
43481             }
43482         } else {
43483             return this.doc.createRange();
43484         }
43485     },
43486     getParentElement: function()
43487     {
43488         
43489         this.assignDocWin();
43490         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43491         
43492         var range = this.createRange(sel);
43493          
43494         try {
43495             var p = range.commonAncestorContainer;
43496             while (p.nodeType == 3) { // text node
43497                 p = p.parentNode;
43498             }
43499             return p;
43500         } catch (e) {
43501             return null;
43502         }
43503     
43504     },
43505     /***
43506      *
43507      * Range intersection.. the hard stuff...
43508      *  '-1' = before
43509      *  '0' = hits..
43510      *  '1' = after.
43511      *         [ -- selected range --- ]
43512      *   [fail]                        [fail]
43513      *
43514      *    basically..
43515      *      if end is before start or  hits it. fail.
43516      *      if start is after end or hits it fail.
43517      *
43518      *   if either hits (but other is outside. - then it's not 
43519      *   
43520      *    
43521      **/
43522     
43523     
43524     // @see http://www.thismuchiknow.co.uk/?p=64.
43525     rangeIntersectsNode : function(range, node)
43526     {
43527         var nodeRange = node.ownerDocument.createRange();
43528         try {
43529             nodeRange.selectNode(node);
43530         } catch (e) {
43531             nodeRange.selectNodeContents(node);
43532         }
43533     
43534         var rangeStartRange = range.cloneRange();
43535         rangeStartRange.collapse(true);
43536     
43537         var rangeEndRange = range.cloneRange();
43538         rangeEndRange.collapse(false);
43539     
43540         var nodeStartRange = nodeRange.cloneRange();
43541         nodeStartRange.collapse(true);
43542     
43543         var nodeEndRange = nodeRange.cloneRange();
43544         nodeEndRange.collapse(false);
43545     
43546         return rangeStartRange.compareBoundaryPoints(
43547                  Range.START_TO_START, nodeEndRange) == -1 &&
43548                rangeEndRange.compareBoundaryPoints(
43549                  Range.START_TO_START, nodeStartRange) == 1;
43550         
43551          
43552     },
43553     rangeCompareNode : function(range, node)
43554     {
43555         var nodeRange = node.ownerDocument.createRange();
43556         try {
43557             nodeRange.selectNode(node);
43558         } catch (e) {
43559             nodeRange.selectNodeContents(node);
43560         }
43561         
43562         
43563         range.collapse(true);
43564     
43565         nodeRange.collapse(true);
43566      
43567         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43568         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43569          
43570         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43571         
43572         var nodeIsBefore   =  ss == 1;
43573         var nodeIsAfter    = ee == -1;
43574         
43575         if (nodeIsBefore && nodeIsAfter) {
43576             return 0; // outer
43577         }
43578         if (!nodeIsBefore && nodeIsAfter) {
43579             return 1; //right trailed.
43580         }
43581         
43582         if (nodeIsBefore && !nodeIsAfter) {
43583             return 2;  // left trailed.
43584         }
43585         // fully contined.
43586         return 3;
43587     },
43588
43589     // private? - in a new class?
43590     cleanUpPaste :  function()
43591     {
43592         // cleans up the whole document..
43593         Roo.log('cleanuppaste');
43594         
43595         this.cleanUpChildren(this.doc.body);
43596         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43597         if (clean != this.doc.body.innerHTML) {
43598             this.doc.body.innerHTML = clean;
43599         }
43600         
43601     },
43602     
43603     cleanWordChars : function(input) {// change the chars to hex code
43604         var he = Roo.HtmlEditorCore;
43605         
43606         var output = input;
43607         Roo.each(he.swapCodes, function(sw) { 
43608             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43609             
43610             output = output.replace(swapper, sw[1]);
43611         });
43612         
43613         return output;
43614     },
43615     
43616     
43617     cleanUpChildren : function (n)
43618     {
43619         if (!n.childNodes.length) {
43620             return;
43621         }
43622         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43623            this.cleanUpChild(n.childNodes[i]);
43624         }
43625     },
43626     
43627     
43628         
43629     
43630     cleanUpChild : function (node)
43631     {
43632         var ed = this;
43633         //console.log(node);
43634         if (node.nodeName == "#text") {
43635             // clean up silly Windows -- stuff?
43636             return; 
43637         }
43638         if (node.nodeName == "#comment") {
43639             node.parentNode.removeChild(node);
43640             // clean up silly Windows -- stuff?
43641             return; 
43642         }
43643         var lcname = node.tagName.toLowerCase();
43644         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43645         // whitelist of tags..
43646         
43647         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43648             // remove node.
43649             node.parentNode.removeChild(node);
43650             return;
43651             
43652         }
43653         
43654         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43655         
43656         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43657         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43658         
43659         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43660         //    remove_keep_children = true;
43661         //}
43662         
43663         if (remove_keep_children) {
43664             this.cleanUpChildren(node);
43665             // inserts everything just before this node...
43666             while (node.childNodes.length) {
43667                 var cn = node.childNodes[0];
43668                 node.removeChild(cn);
43669                 node.parentNode.insertBefore(cn, node);
43670             }
43671             node.parentNode.removeChild(node);
43672             return;
43673         }
43674         
43675         if (!node.attributes || !node.attributes.length) {
43676             this.cleanUpChildren(node);
43677             return;
43678         }
43679         
43680         function cleanAttr(n,v)
43681         {
43682             
43683             if (v.match(/^\./) || v.match(/^\//)) {
43684                 return;
43685             }
43686             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43687                 return;
43688             }
43689             if (v.match(/^#/)) {
43690                 return;
43691             }
43692 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43693             node.removeAttribute(n);
43694             
43695         }
43696         
43697         var cwhite = this.cwhite;
43698         var cblack = this.cblack;
43699             
43700         function cleanStyle(n,v)
43701         {
43702             if (v.match(/expression/)) { //XSS?? should we even bother..
43703                 node.removeAttribute(n);
43704                 return;
43705             }
43706             
43707             var parts = v.split(/;/);
43708             var clean = [];
43709             
43710             Roo.each(parts, function(p) {
43711                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43712                 if (!p.length) {
43713                     return true;
43714                 }
43715                 var l = p.split(':').shift().replace(/\s+/g,'');
43716                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43717                 
43718                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43719 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43720                     //node.removeAttribute(n);
43721                     return true;
43722                 }
43723                 //Roo.log()
43724                 // only allow 'c whitelisted system attributes'
43725                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43726 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43727                     //node.removeAttribute(n);
43728                     return true;
43729                 }
43730                 
43731                 
43732                  
43733                 
43734                 clean.push(p);
43735                 return true;
43736             });
43737             if (clean.length) { 
43738                 node.setAttribute(n, clean.join(';'));
43739             } else {
43740                 node.removeAttribute(n);
43741             }
43742             
43743         }
43744         
43745         
43746         for (var i = node.attributes.length-1; i > -1 ; i--) {
43747             var a = node.attributes[i];
43748             //console.log(a);
43749             
43750             if (a.name.toLowerCase().substr(0,2)=='on')  {
43751                 node.removeAttribute(a.name);
43752                 continue;
43753             }
43754             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43755                 node.removeAttribute(a.name);
43756                 continue;
43757             }
43758             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43759                 cleanAttr(a.name,a.value); // fixme..
43760                 continue;
43761             }
43762             if (a.name == 'style') {
43763                 cleanStyle(a.name,a.value);
43764                 continue;
43765             }
43766             /// clean up MS crap..
43767             // tecnically this should be a list of valid class'es..
43768             
43769             
43770             if (a.name == 'class') {
43771                 if (a.value.match(/^Mso/)) {
43772                     node.className = '';
43773                 }
43774                 
43775                 if (a.value.match(/body/)) {
43776                     node.className = '';
43777                 }
43778                 continue;
43779             }
43780             
43781             // style cleanup!?
43782             // class cleanup?
43783             
43784         }
43785         
43786         
43787         this.cleanUpChildren(node);
43788         
43789         
43790     },
43791     
43792     /**
43793      * Clean up MS wordisms...
43794      */
43795     cleanWord : function(node)
43796     {
43797         
43798         
43799         if (!node) {
43800             this.cleanWord(this.doc.body);
43801             return;
43802         }
43803         if (node.nodeName == "#text") {
43804             // clean up silly Windows -- stuff?
43805             return; 
43806         }
43807         if (node.nodeName == "#comment") {
43808             node.parentNode.removeChild(node);
43809             // clean up silly Windows -- stuff?
43810             return; 
43811         }
43812         
43813         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43814             node.parentNode.removeChild(node);
43815             return;
43816         }
43817         
43818         // remove - but keep children..
43819         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43820             while (node.childNodes.length) {
43821                 var cn = node.childNodes[0];
43822                 node.removeChild(cn);
43823                 node.parentNode.insertBefore(cn, node);
43824             }
43825             node.parentNode.removeChild(node);
43826             this.iterateChildren(node, this.cleanWord);
43827             return;
43828         }
43829         // clean styles
43830         if (node.className.length) {
43831             
43832             var cn = node.className.split(/\W+/);
43833             var cna = [];
43834             Roo.each(cn, function(cls) {
43835                 if (cls.match(/Mso[a-zA-Z]+/)) {
43836                     return;
43837                 }
43838                 cna.push(cls);
43839             });
43840             node.className = cna.length ? cna.join(' ') : '';
43841             if (!cna.length) {
43842                 node.removeAttribute("class");
43843             }
43844         }
43845         
43846         if (node.hasAttribute("lang")) {
43847             node.removeAttribute("lang");
43848         }
43849         
43850         if (node.hasAttribute("style")) {
43851             
43852             var styles = node.getAttribute("style").split(";");
43853             var nstyle = [];
43854             Roo.each(styles, function(s) {
43855                 if (!s.match(/:/)) {
43856                     return;
43857                 }
43858                 var kv = s.split(":");
43859                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43860                     return;
43861                 }
43862                 // what ever is left... we allow.
43863                 nstyle.push(s);
43864             });
43865             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43866             if (!nstyle.length) {
43867                 node.removeAttribute('style');
43868             }
43869         }
43870         this.iterateChildren(node, this.cleanWord);
43871         
43872         
43873         
43874     },
43875     /**
43876      * iterateChildren of a Node, calling fn each time, using this as the scole..
43877      * @param {DomNode} node node to iterate children of.
43878      * @param {Function} fn method of this class to call on each item.
43879      */
43880     iterateChildren : function(node, fn)
43881     {
43882         if (!node.childNodes.length) {
43883                 return;
43884         }
43885         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43886            fn.call(this, node.childNodes[i])
43887         }
43888     },
43889     
43890     
43891     /**
43892      * cleanTableWidths.
43893      *
43894      * Quite often pasting from word etc.. results in tables with column and widths.
43895      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43896      *
43897      */
43898     cleanTableWidths : function(node)
43899     {
43900          
43901          
43902         if (!node) {
43903             this.cleanTableWidths(this.doc.body);
43904             return;
43905         }
43906         
43907         // ignore list...
43908         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43909             return; 
43910         }
43911         Roo.log(node.tagName);
43912         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43913             this.iterateChildren(node, this.cleanTableWidths);
43914             return;
43915         }
43916         if (node.hasAttribute('width')) {
43917             node.removeAttribute('width');
43918         }
43919         
43920          
43921         if (node.hasAttribute("style")) {
43922             // pretty basic...
43923             
43924             var styles = node.getAttribute("style").split(";");
43925             var nstyle = [];
43926             Roo.each(styles, function(s) {
43927                 if (!s.match(/:/)) {
43928                     return;
43929                 }
43930                 var kv = s.split(":");
43931                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43932                     return;
43933                 }
43934                 // what ever is left... we allow.
43935                 nstyle.push(s);
43936             });
43937             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43938             if (!nstyle.length) {
43939                 node.removeAttribute('style');
43940             }
43941         }
43942         
43943         this.iterateChildren(node, this.cleanTableWidths);
43944         
43945         
43946     },
43947     
43948     
43949     
43950     
43951     domToHTML : function(currentElement, depth, nopadtext) {
43952         
43953         depth = depth || 0;
43954         nopadtext = nopadtext || false;
43955     
43956         if (!currentElement) {
43957             return this.domToHTML(this.doc.body);
43958         }
43959         
43960         //Roo.log(currentElement);
43961         var j;
43962         var allText = false;
43963         var nodeName = currentElement.nodeName;
43964         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
43965         
43966         if  (nodeName == '#text') {
43967             
43968             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
43969         }
43970         
43971         
43972         var ret = '';
43973         if (nodeName != 'BODY') {
43974              
43975             var i = 0;
43976             // Prints the node tagName, such as <A>, <IMG>, etc
43977             if (tagName) {
43978                 var attr = [];
43979                 for(i = 0; i < currentElement.attributes.length;i++) {
43980                     // quoting?
43981                     var aname = currentElement.attributes.item(i).name;
43982                     if (!currentElement.attributes.item(i).value.length) {
43983                         continue;
43984                     }
43985                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
43986                 }
43987                 
43988                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
43989             } 
43990             else {
43991                 
43992                 // eack
43993             }
43994         } else {
43995             tagName = false;
43996         }
43997         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
43998             return ret;
43999         }
44000         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44001             nopadtext = true;
44002         }
44003         
44004         
44005         // Traverse the tree
44006         i = 0;
44007         var currentElementChild = currentElement.childNodes.item(i);
44008         var allText = true;
44009         var innerHTML  = '';
44010         lastnode = '';
44011         while (currentElementChild) {
44012             // Formatting code (indent the tree so it looks nice on the screen)
44013             var nopad = nopadtext;
44014             if (lastnode == 'SPAN') {
44015                 nopad  = true;
44016             }
44017             // text
44018             if  (currentElementChild.nodeName == '#text') {
44019                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44020                 toadd = nopadtext ? toadd : toadd.trim();
44021                 if (!nopad && toadd.length > 80) {
44022                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44023                 }
44024                 innerHTML  += toadd;
44025                 
44026                 i++;
44027                 currentElementChild = currentElement.childNodes.item(i);
44028                 lastNode = '';
44029                 continue;
44030             }
44031             allText = false;
44032             
44033             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44034                 
44035             // Recursively traverse the tree structure of the child node
44036             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44037             lastnode = currentElementChild.nodeName;
44038             i++;
44039             currentElementChild=currentElement.childNodes.item(i);
44040         }
44041         
44042         ret += innerHTML;
44043         
44044         if (!allText) {
44045                 // The remaining code is mostly for formatting the tree
44046             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44047         }
44048         
44049         
44050         if (tagName) {
44051             ret+= "</"+tagName+">";
44052         }
44053         return ret;
44054         
44055     },
44056         
44057     applyBlacklists : function()
44058     {
44059         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44060         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44061         
44062         this.white = [];
44063         this.black = [];
44064         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44065             if (b.indexOf(tag) > -1) {
44066                 return;
44067             }
44068             this.white.push(tag);
44069             
44070         }, this);
44071         
44072         Roo.each(w, function(tag) {
44073             if (b.indexOf(tag) > -1) {
44074                 return;
44075             }
44076             if (this.white.indexOf(tag) > -1) {
44077                 return;
44078             }
44079             this.white.push(tag);
44080             
44081         }, this);
44082         
44083         
44084         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44085             if (w.indexOf(tag) > -1) {
44086                 return;
44087             }
44088             this.black.push(tag);
44089             
44090         }, this);
44091         
44092         Roo.each(b, function(tag) {
44093             if (w.indexOf(tag) > -1) {
44094                 return;
44095             }
44096             if (this.black.indexOf(tag) > -1) {
44097                 return;
44098             }
44099             this.black.push(tag);
44100             
44101         }, this);
44102         
44103         
44104         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44105         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44106         
44107         this.cwhite = [];
44108         this.cblack = [];
44109         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44110             if (b.indexOf(tag) > -1) {
44111                 return;
44112             }
44113             this.cwhite.push(tag);
44114             
44115         }, this);
44116         
44117         Roo.each(w, function(tag) {
44118             if (b.indexOf(tag) > -1) {
44119                 return;
44120             }
44121             if (this.cwhite.indexOf(tag) > -1) {
44122                 return;
44123             }
44124             this.cwhite.push(tag);
44125             
44126         }, this);
44127         
44128         
44129         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44130             if (w.indexOf(tag) > -1) {
44131                 return;
44132             }
44133             this.cblack.push(tag);
44134             
44135         }, this);
44136         
44137         Roo.each(b, function(tag) {
44138             if (w.indexOf(tag) > -1) {
44139                 return;
44140             }
44141             if (this.cblack.indexOf(tag) > -1) {
44142                 return;
44143             }
44144             this.cblack.push(tag);
44145             
44146         }, this);
44147     },
44148     
44149     setStylesheets : function(stylesheets)
44150     {
44151         if(typeof(stylesheets) == 'string'){
44152             Roo.get(this.iframe.contentDocument.head).createChild({
44153                 tag : 'link',
44154                 rel : 'stylesheet',
44155                 type : 'text/css',
44156                 href : stylesheets
44157             });
44158             
44159             return;
44160         }
44161         var _this = this;
44162      
44163         Roo.each(stylesheets, function(s) {
44164             if(!s.length){
44165                 return;
44166             }
44167             
44168             Roo.get(_this.iframe.contentDocument.head).createChild({
44169                 tag : 'link',
44170                 rel : 'stylesheet',
44171                 type : 'text/css',
44172                 href : s
44173             });
44174         });
44175
44176         
44177     },
44178     
44179     removeStylesheets : function()
44180     {
44181         var _this = this;
44182         
44183         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44184             s.remove();
44185         });
44186     }
44187     
44188     // hide stuff that is not compatible
44189     /**
44190      * @event blur
44191      * @hide
44192      */
44193     /**
44194      * @event change
44195      * @hide
44196      */
44197     /**
44198      * @event focus
44199      * @hide
44200      */
44201     /**
44202      * @event specialkey
44203      * @hide
44204      */
44205     /**
44206      * @cfg {String} fieldClass @hide
44207      */
44208     /**
44209      * @cfg {String} focusClass @hide
44210      */
44211     /**
44212      * @cfg {String} autoCreate @hide
44213      */
44214     /**
44215      * @cfg {String} inputType @hide
44216      */
44217     /**
44218      * @cfg {String} invalidClass @hide
44219      */
44220     /**
44221      * @cfg {String} invalidText @hide
44222      */
44223     /**
44224      * @cfg {String} msgFx @hide
44225      */
44226     /**
44227      * @cfg {String} validateOnBlur @hide
44228      */
44229 });
44230
44231 Roo.HtmlEditorCore.white = [
44232         'area', 'br', 'img', 'input', 'hr', 'wbr',
44233         
44234        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44235        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44236        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44237        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44238        'table',   'ul',         'xmp', 
44239        
44240        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44241       'thead',   'tr', 
44242      
44243       'dir', 'menu', 'ol', 'ul', 'dl',
44244        
44245       'embed',  'object'
44246 ];
44247
44248
44249 Roo.HtmlEditorCore.black = [
44250     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44251         'applet', // 
44252         'base',   'basefont', 'bgsound', 'blink',  'body', 
44253         'frame',  'frameset', 'head',    'html',   'ilayer', 
44254         'iframe', 'layer',  'link',     'meta',    'object',   
44255         'script', 'style' ,'title',  'xml' // clean later..
44256 ];
44257 Roo.HtmlEditorCore.clean = [
44258     'script', 'style', 'title', 'xml'
44259 ];
44260 Roo.HtmlEditorCore.remove = [
44261     'font'
44262 ];
44263 // attributes..
44264
44265 Roo.HtmlEditorCore.ablack = [
44266     'on'
44267 ];
44268     
44269 Roo.HtmlEditorCore.aclean = [ 
44270     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44271 ];
44272
44273 // protocols..
44274 Roo.HtmlEditorCore.pwhite= [
44275         'http',  'https',  'mailto'
44276 ];
44277
44278 // white listed style attributes.
44279 Roo.HtmlEditorCore.cwhite= [
44280       //  'text-align', /// default is to allow most things..
44281       
44282          
44283 //        'font-size'//??
44284 ];
44285
44286 // black listed style attributes.
44287 Roo.HtmlEditorCore.cblack= [
44288       //  'font-size' -- this can be set by the project 
44289 ];
44290
44291
44292 Roo.HtmlEditorCore.swapCodes   =[ 
44293     [    8211, "--" ], 
44294     [    8212, "--" ], 
44295     [    8216,  "'" ],  
44296     [    8217, "'" ],  
44297     [    8220, '"' ],  
44298     [    8221, '"' ],  
44299     [    8226, "*" ],  
44300     [    8230, "..." ]
44301 ]; 
44302
44303     //<script type="text/javascript">
44304
44305 /*
44306  * Ext JS Library 1.1.1
44307  * Copyright(c) 2006-2007, Ext JS, LLC.
44308  * Licence LGPL
44309  * 
44310  */
44311  
44312  
44313 Roo.form.HtmlEditor = function(config){
44314     
44315     
44316     
44317     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44318     
44319     if (!this.toolbars) {
44320         this.toolbars = [];
44321     }
44322     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44323     
44324     
44325 };
44326
44327 /**
44328  * @class Roo.form.HtmlEditor
44329  * @extends Roo.form.Field
44330  * Provides a lightweight HTML Editor component.
44331  *
44332  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44333  * 
44334  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44335  * supported by this editor.</b><br/><br/>
44336  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44337  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44338  */
44339 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44340     /**
44341      * @cfg {Boolean} clearUp
44342      */
44343     clearUp : true,
44344       /**
44345      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44346      */
44347     toolbars : false,
44348    
44349      /**
44350      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44351      *                        Roo.resizable.
44352      */
44353     resizable : false,
44354      /**
44355      * @cfg {Number} height (in pixels)
44356      */   
44357     height: 300,
44358    /**
44359      * @cfg {Number} width (in pixels)
44360      */   
44361     width: 500,
44362     
44363     /**
44364      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44365      * 
44366      */
44367     stylesheets: false,
44368     
44369     
44370      /**
44371      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44372      * 
44373      */
44374     cblack: false,
44375     /**
44376      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44377      * 
44378      */
44379     cwhite: false,
44380     
44381      /**
44382      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44383      * 
44384      */
44385     black: false,
44386     /**
44387      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44388      * 
44389      */
44390     white: false,
44391     
44392     // id of frame..
44393     frameId: false,
44394     
44395     // private properties
44396     validationEvent : false,
44397     deferHeight: true,
44398     initialized : false,
44399     activated : false,
44400     
44401     onFocus : Roo.emptyFn,
44402     iframePad:3,
44403     hideMode:'offsets',
44404     
44405     actionMode : 'container', // defaults to hiding it...
44406     
44407     defaultAutoCreate : { // modified by initCompnoent..
44408         tag: "textarea",
44409         style:"width:500px;height:300px;",
44410         autocomplete: "new-password"
44411     },
44412
44413     // private
44414     initComponent : function(){
44415         this.addEvents({
44416             /**
44417              * @event initialize
44418              * Fires when the editor is fully initialized (including the iframe)
44419              * @param {HtmlEditor} this
44420              */
44421             initialize: true,
44422             /**
44423              * @event activate
44424              * Fires when the editor is first receives the focus. Any insertion must wait
44425              * until after this event.
44426              * @param {HtmlEditor} this
44427              */
44428             activate: true,
44429              /**
44430              * @event beforesync
44431              * Fires before the textarea is updated with content from the editor iframe. Return false
44432              * to cancel the sync.
44433              * @param {HtmlEditor} this
44434              * @param {String} html
44435              */
44436             beforesync: true,
44437              /**
44438              * @event beforepush
44439              * Fires before the iframe editor is updated with content from the textarea. Return false
44440              * to cancel the push.
44441              * @param {HtmlEditor} this
44442              * @param {String} html
44443              */
44444             beforepush: true,
44445              /**
44446              * @event sync
44447              * Fires when the textarea is updated with content from the editor iframe.
44448              * @param {HtmlEditor} this
44449              * @param {String} html
44450              */
44451             sync: true,
44452              /**
44453              * @event push
44454              * Fires when the iframe editor is updated with content from the textarea.
44455              * @param {HtmlEditor} this
44456              * @param {String} html
44457              */
44458             push: true,
44459              /**
44460              * @event editmodechange
44461              * Fires when the editor switches edit modes
44462              * @param {HtmlEditor} this
44463              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44464              */
44465             editmodechange: true,
44466             /**
44467              * @event editorevent
44468              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44469              * @param {HtmlEditor} this
44470              */
44471             editorevent: true,
44472             /**
44473              * @event firstfocus
44474              * Fires when on first focus - needed by toolbars..
44475              * @param {HtmlEditor} this
44476              */
44477             firstfocus: true,
44478             /**
44479              * @event autosave
44480              * Auto save the htmlEditor value as a file into Events
44481              * @param {HtmlEditor} this
44482              */
44483             autosave: true,
44484             /**
44485              * @event savedpreview
44486              * preview the saved version of htmlEditor
44487              * @param {HtmlEditor} this
44488              */
44489             savedpreview: true,
44490             
44491             /**
44492             * @event stylesheetsclick
44493             * Fires when press the Sytlesheets button
44494             * @param {Roo.HtmlEditorCore} this
44495             */
44496             stylesheetsclick: true
44497         });
44498         this.defaultAutoCreate =  {
44499             tag: "textarea",
44500             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44501             autocomplete: "new-password"
44502         };
44503     },
44504
44505     /**
44506      * Protected method that will not generally be called directly. It
44507      * is called when the editor creates its toolbar. Override this method if you need to
44508      * add custom toolbar buttons.
44509      * @param {HtmlEditor} editor
44510      */
44511     createToolbar : function(editor){
44512         Roo.log("create toolbars");
44513         if (!editor.toolbars || !editor.toolbars.length) {
44514             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44515         }
44516         
44517         for (var i =0 ; i < editor.toolbars.length;i++) {
44518             editor.toolbars[i] = Roo.factory(
44519                     typeof(editor.toolbars[i]) == 'string' ?
44520                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44521                 Roo.form.HtmlEditor);
44522             editor.toolbars[i].init(editor);
44523         }
44524          
44525         
44526     },
44527
44528      
44529     // private
44530     onRender : function(ct, position)
44531     {
44532         var _t = this;
44533         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44534         
44535         this.wrap = this.el.wrap({
44536             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44537         });
44538         
44539         this.editorcore.onRender(ct, position);
44540          
44541         if (this.resizable) {
44542             this.resizeEl = new Roo.Resizable(this.wrap, {
44543                 pinned : true,
44544                 wrap: true,
44545                 dynamic : true,
44546                 minHeight : this.height,
44547                 height: this.height,
44548                 handles : this.resizable,
44549                 width: this.width,
44550                 listeners : {
44551                     resize : function(r, w, h) {
44552                         _t.onResize(w,h); // -something
44553                     }
44554                 }
44555             });
44556             
44557         }
44558         this.createToolbar(this);
44559        
44560         
44561         if(!this.width){
44562             this.setSize(this.wrap.getSize());
44563         }
44564         if (this.resizeEl) {
44565             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44566             // should trigger onReize..
44567         }
44568         
44569         this.keyNav = new Roo.KeyNav(this.el, {
44570             
44571             "tab" : function(e){
44572                 e.preventDefault();
44573                 
44574                 var value = this.getValue();
44575                 
44576                 var start = this.el.dom.selectionStart;
44577                 var end = this.el.dom.selectionEnd;
44578                 
44579                 if(!e.shiftKey){
44580                     
44581                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44582                     this.el.dom.setSelectionRange(end + 1, end + 1);
44583                     return;
44584                 }
44585                 
44586                 var f = value.substring(0, start).split("\t");
44587                 
44588                 if(f.pop().length != 0){
44589                     return;
44590                 }
44591                 
44592                 this.setValue(f.join("\t") + value.substring(end));
44593                 this.el.dom.setSelectionRange(start - 1, start - 1);
44594                 
44595             },
44596             
44597             "home" : function(e){
44598                 e.preventDefault();
44599                 
44600                 var curr = this.el.dom.selectionStart;
44601                 var lines = this.getValue().split("\n");
44602                 
44603                 if(!lines.length){
44604                     return;
44605                 }
44606                 
44607                 if(e.ctrlKey){
44608                     this.el.dom.setSelectionRange(0, 0);
44609                     return;
44610                 }
44611                 
44612                 var pos = 0;
44613                 
44614                 for (var i = 0; i < lines.length;i++) {
44615                     pos += lines[i].length;
44616                     
44617                     if(i != 0){
44618                         pos += 1;
44619                     }
44620                     
44621                     if(pos < curr){
44622                         continue;
44623                     }
44624                     
44625                     pos -= lines[i].length;
44626                     
44627                     break;
44628                 }
44629                 
44630                 if(!e.shiftKey){
44631                     this.el.dom.setSelectionRange(pos, pos);
44632                     return;
44633                 }
44634                 
44635                 this.el.dom.selectionStart = pos;
44636                 this.el.dom.selectionEnd = curr;
44637             },
44638             
44639             "end" : function(e){
44640                 e.preventDefault();
44641                 
44642                 var curr = this.el.dom.selectionStart;
44643                 var lines = this.getValue().split("\n");
44644                 
44645                 if(!lines.length){
44646                     return;
44647                 }
44648                 
44649                 if(e.ctrlKey){
44650                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44651                     return;
44652                 }
44653                 
44654                 var pos = 0;
44655                 
44656                 for (var i = 0; i < lines.length;i++) {
44657                     
44658                     pos += lines[i].length;
44659                     
44660                     if(i != 0){
44661                         pos += 1;
44662                     }
44663                     
44664                     if(pos < curr){
44665                         continue;
44666                     }
44667                     
44668                     break;
44669                 }
44670                 
44671                 if(!e.shiftKey){
44672                     this.el.dom.setSelectionRange(pos, pos);
44673                     return;
44674                 }
44675                 
44676                 this.el.dom.selectionStart = curr;
44677                 this.el.dom.selectionEnd = pos;
44678             },
44679
44680             scope : this,
44681
44682             doRelay : function(foo, bar, hname){
44683                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44684             },
44685
44686             forceKeyDown: true
44687         });
44688         
44689 //        if(this.autosave && this.w){
44690 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44691 //        }
44692     },
44693
44694     // private
44695     onResize : function(w, h)
44696     {
44697         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44698         var ew = false;
44699         var eh = false;
44700         
44701         if(this.el ){
44702             if(typeof w == 'number'){
44703                 var aw = w - this.wrap.getFrameWidth('lr');
44704                 this.el.setWidth(this.adjustWidth('textarea', aw));
44705                 ew = aw;
44706             }
44707             if(typeof h == 'number'){
44708                 var tbh = 0;
44709                 for (var i =0; i < this.toolbars.length;i++) {
44710                     // fixme - ask toolbars for heights?
44711                     tbh += this.toolbars[i].tb.el.getHeight();
44712                     if (this.toolbars[i].footer) {
44713                         tbh += this.toolbars[i].footer.el.getHeight();
44714                     }
44715                 }
44716                 
44717                 
44718                 
44719                 
44720                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44721                 ah -= 5; // knock a few pixes off for look..
44722 //                Roo.log(ah);
44723                 this.el.setHeight(this.adjustWidth('textarea', ah));
44724                 var eh = ah;
44725             }
44726         }
44727         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44728         this.editorcore.onResize(ew,eh);
44729         
44730     },
44731
44732     /**
44733      * Toggles the editor between standard and source edit mode.
44734      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44735      */
44736     toggleSourceEdit : function(sourceEditMode)
44737     {
44738         this.editorcore.toggleSourceEdit(sourceEditMode);
44739         
44740         if(this.editorcore.sourceEditMode){
44741             Roo.log('editor - showing textarea');
44742             
44743 //            Roo.log('in');
44744 //            Roo.log(this.syncValue());
44745             this.editorcore.syncValue();
44746             this.el.removeClass('x-hidden');
44747             this.el.dom.removeAttribute('tabIndex');
44748             this.el.focus();
44749             
44750             for (var i = 0; i < this.toolbars.length; i++) {
44751                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44752                     this.toolbars[i].tb.hide();
44753                     this.toolbars[i].footer.hide();
44754                 }
44755             }
44756             
44757         }else{
44758             Roo.log('editor - hiding textarea');
44759 //            Roo.log('out')
44760 //            Roo.log(this.pushValue()); 
44761             this.editorcore.pushValue();
44762             
44763             this.el.addClass('x-hidden');
44764             this.el.dom.setAttribute('tabIndex', -1);
44765             
44766             for (var i = 0; i < this.toolbars.length; i++) {
44767                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44768                     this.toolbars[i].tb.show();
44769                     this.toolbars[i].footer.show();
44770                 }
44771             }
44772             
44773             //this.deferFocus();
44774         }
44775         
44776         this.setSize(this.wrap.getSize());
44777         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44778         
44779         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44780     },
44781  
44782     // private (for BoxComponent)
44783     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44784
44785     // private (for BoxComponent)
44786     getResizeEl : function(){
44787         return this.wrap;
44788     },
44789
44790     // private (for BoxComponent)
44791     getPositionEl : function(){
44792         return this.wrap;
44793     },
44794
44795     // private
44796     initEvents : function(){
44797         this.originalValue = this.getValue();
44798     },
44799
44800     /**
44801      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44802      * @method
44803      */
44804     markInvalid : Roo.emptyFn,
44805     /**
44806      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44807      * @method
44808      */
44809     clearInvalid : Roo.emptyFn,
44810
44811     setValue : function(v){
44812         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44813         this.editorcore.pushValue();
44814     },
44815
44816      
44817     // private
44818     deferFocus : function(){
44819         this.focus.defer(10, this);
44820     },
44821
44822     // doc'ed in Field
44823     focus : function(){
44824         this.editorcore.focus();
44825         
44826     },
44827       
44828
44829     // private
44830     onDestroy : function(){
44831         
44832         
44833         
44834         if(this.rendered){
44835             
44836             for (var i =0; i < this.toolbars.length;i++) {
44837                 // fixme - ask toolbars for heights?
44838                 this.toolbars[i].onDestroy();
44839             }
44840             
44841             this.wrap.dom.innerHTML = '';
44842             this.wrap.remove();
44843         }
44844     },
44845
44846     // private
44847     onFirstFocus : function(){
44848         //Roo.log("onFirstFocus");
44849         this.editorcore.onFirstFocus();
44850          for (var i =0; i < this.toolbars.length;i++) {
44851             this.toolbars[i].onFirstFocus();
44852         }
44853         
44854     },
44855     
44856     // private
44857     syncValue : function()
44858     {
44859         this.editorcore.syncValue();
44860     },
44861     
44862     pushValue : function()
44863     {
44864         this.editorcore.pushValue();
44865     },
44866     
44867     setStylesheets : function(stylesheets)
44868     {
44869         this.editorcore.setStylesheets(stylesheets);
44870     },
44871     
44872     removeStylesheets : function()
44873     {
44874         this.editorcore.removeStylesheets();
44875     }
44876      
44877     
44878     // hide stuff that is not compatible
44879     /**
44880      * @event blur
44881      * @hide
44882      */
44883     /**
44884      * @event change
44885      * @hide
44886      */
44887     /**
44888      * @event focus
44889      * @hide
44890      */
44891     /**
44892      * @event specialkey
44893      * @hide
44894      */
44895     /**
44896      * @cfg {String} fieldClass @hide
44897      */
44898     /**
44899      * @cfg {String} focusClass @hide
44900      */
44901     /**
44902      * @cfg {String} autoCreate @hide
44903      */
44904     /**
44905      * @cfg {String} inputType @hide
44906      */
44907     /**
44908      * @cfg {String} invalidClass @hide
44909      */
44910     /**
44911      * @cfg {String} invalidText @hide
44912      */
44913     /**
44914      * @cfg {String} msgFx @hide
44915      */
44916     /**
44917      * @cfg {String} validateOnBlur @hide
44918      */
44919 });
44920  
44921     // <script type="text/javascript">
44922 /*
44923  * Based on
44924  * Ext JS Library 1.1.1
44925  * Copyright(c) 2006-2007, Ext JS, LLC.
44926  *  
44927  
44928  */
44929
44930 /**
44931  * @class Roo.form.HtmlEditorToolbar1
44932  * Basic Toolbar
44933  * 
44934  * Usage:
44935  *
44936  new Roo.form.HtmlEditor({
44937     ....
44938     toolbars : [
44939         new Roo.form.HtmlEditorToolbar1({
44940             disable : { fonts: 1 , format: 1, ..., ... , ...],
44941             btns : [ .... ]
44942         })
44943     }
44944      
44945  * 
44946  * @cfg {Object} disable List of elements to disable..
44947  * @cfg {Array} btns List of additional buttons.
44948  * 
44949  * 
44950  * NEEDS Extra CSS? 
44951  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44952  */
44953  
44954 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44955 {
44956     
44957     Roo.apply(this, config);
44958     
44959     // default disabled, based on 'good practice'..
44960     this.disable = this.disable || {};
44961     Roo.applyIf(this.disable, {
44962         fontSize : true,
44963         colors : true,
44964         specialElements : true
44965     });
44966     
44967     
44968     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44969     // dont call parent... till later.
44970 }
44971
44972 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
44973     
44974     tb: false,
44975     
44976     rendered: false,
44977     
44978     editor : false,
44979     editorcore : false,
44980     /**
44981      * @cfg {Object} disable  List of toolbar elements to disable
44982          
44983      */
44984     disable : false,
44985     
44986     
44987      /**
44988      * @cfg {String} createLinkText The default text for the create link prompt
44989      */
44990     createLinkText : 'Please enter the URL for the link:',
44991     /**
44992      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
44993      */
44994     defaultLinkValue : 'http:/'+'/',
44995    
44996     
44997       /**
44998      * @cfg {Array} fontFamilies An array of available font families
44999      */
45000     fontFamilies : [
45001         'Arial',
45002         'Courier New',
45003         'Tahoma',
45004         'Times New Roman',
45005         'Verdana'
45006     ],
45007     
45008     specialChars : [
45009            "&#169;",
45010           "&#174;",     
45011           "&#8482;",    
45012           "&#163;" ,    
45013          // "&#8212;",    
45014           "&#8230;",    
45015           "&#247;" ,    
45016         //  "&#225;" ,     ?? a acute?
45017            "&#8364;"    , //Euro
45018        //   "&#8220;"    ,
45019         //  "&#8221;"    ,
45020         //  "&#8226;"    ,
45021           "&#176;"  //   , // degrees
45022
45023          // "&#233;"     , // e ecute
45024          // "&#250;"     , // u ecute?
45025     ],
45026     
45027     specialElements : [
45028         {
45029             text: "Insert Table",
45030             xtype: 'MenuItem',
45031             xns : Roo.Menu,
45032             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45033                 
45034         },
45035         {    
45036             text: "Insert Image",
45037             xtype: 'MenuItem',
45038             xns : Roo.Menu,
45039             ihtml : '<img src="about:blank"/>'
45040             
45041         }
45042         
45043          
45044     ],
45045     
45046     
45047     inputElements : [ 
45048             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45049             "input:submit", "input:button", "select", "textarea", "label" ],
45050     formats : [
45051         ["p"] ,  
45052         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45053         ["pre"],[ "code"], 
45054         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45055         ['div'],['span']
45056     ],
45057     
45058     cleanStyles : [
45059         "font-size"
45060     ],
45061      /**
45062      * @cfg {String} defaultFont default font to use.
45063      */
45064     defaultFont: 'tahoma',
45065    
45066     fontSelect : false,
45067     
45068     
45069     formatCombo : false,
45070     
45071     init : function(editor)
45072     {
45073         this.editor = editor;
45074         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45075         var editorcore = this.editorcore;
45076         
45077         var _t = this;
45078         
45079         var fid = editorcore.frameId;
45080         var etb = this;
45081         function btn(id, toggle, handler){
45082             var xid = fid + '-'+ id ;
45083             return {
45084                 id : xid,
45085                 cmd : id,
45086                 cls : 'x-btn-icon x-edit-'+id,
45087                 enableToggle:toggle !== false,
45088                 scope: _t, // was editor...
45089                 handler:handler||_t.relayBtnCmd,
45090                 clickEvent:'mousedown',
45091                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45092                 tabIndex:-1
45093             };
45094         }
45095         
45096         
45097         
45098         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45099         this.tb = tb;
45100          // stop form submits
45101         tb.el.on('click', function(e){
45102             e.preventDefault(); // what does this do?
45103         });
45104
45105         if(!this.disable.font) { // && !Roo.isSafari){
45106             /* why no safari for fonts 
45107             editor.fontSelect = tb.el.createChild({
45108                 tag:'select',
45109                 tabIndex: -1,
45110                 cls:'x-font-select',
45111                 html: this.createFontOptions()
45112             });
45113             
45114             editor.fontSelect.on('change', function(){
45115                 var font = editor.fontSelect.dom.value;
45116                 editor.relayCmd('fontname', font);
45117                 editor.deferFocus();
45118             }, editor);
45119             
45120             tb.add(
45121                 editor.fontSelect.dom,
45122                 '-'
45123             );
45124             */
45125             
45126         };
45127         if(!this.disable.formats){
45128             this.formatCombo = new Roo.form.ComboBox({
45129                 store: new Roo.data.SimpleStore({
45130                     id : 'tag',
45131                     fields: ['tag'],
45132                     data : this.formats // from states.js
45133                 }),
45134                 blockFocus : true,
45135                 name : '',
45136                 //autoCreate : {tag: "div",  size: "20"},
45137                 displayField:'tag',
45138                 typeAhead: false,
45139                 mode: 'local',
45140                 editable : false,
45141                 triggerAction: 'all',
45142                 emptyText:'Add tag',
45143                 selectOnFocus:true,
45144                 width:135,
45145                 listeners : {
45146                     'select': function(c, r, i) {
45147                         editorcore.insertTag(r.get('tag'));
45148                         editor.focus();
45149                     }
45150                 }
45151
45152             });
45153             tb.addField(this.formatCombo);
45154             
45155         }
45156         
45157         if(!this.disable.format){
45158             tb.add(
45159                 btn('bold'),
45160                 btn('italic'),
45161                 btn('underline'),
45162                 btn('strikethrough')
45163             );
45164         };
45165         if(!this.disable.fontSize){
45166             tb.add(
45167                 '-',
45168                 
45169                 
45170                 btn('increasefontsize', false, editorcore.adjustFont),
45171                 btn('decreasefontsize', false, editorcore.adjustFont)
45172             );
45173         };
45174         
45175         
45176         if(!this.disable.colors){
45177             tb.add(
45178                 '-', {
45179                     id:editorcore.frameId +'-forecolor',
45180                     cls:'x-btn-icon x-edit-forecolor',
45181                     clickEvent:'mousedown',
45182                     tooltip: this.buttonTips['forecolor'] || undefined,
45183                     tabIndex:-1,
45184                     menu : new Roo.menu.ColorMenu({
45185                         allowReselect: true,
45186                         focus: Roo.emptyFn,
45187                         value:'000000',
45188                         plain:true,
45189                         selectHandler: function(cp, color){
45190                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45191                             editor.deferFocus();
45192                         },
45193                         scope: editorcore,
45194                         clickEvent:'mousedown'
45195                     })
45196                 }, {
45197                     id:editorcore.frameId +'backcolor',
45198                     cls:'x-btn-icon x-edit-backcolor',
45199                     clickEvent:'mousedown',
45200                     tooltip: this.buttonTips['backcolor'] || undefined,
45201                     tabIndex:-1,
45202                     menu : new Roo.menu.ColorMenu({
45203                         focus: Roo.emptyFn,
45204                         value:'FFFFFF',
45205                         plain:true,
45206                         allowReselect: true,
45207                         selectHandler: function(cp, color){
45208                             if(Roo.isGecko){
45209                                 editorcore.execCmd('useCSS', false);
45210                                 editorcore.execCmd('hilitecolor', color);
45211                                 editorcore.execCmd('useCSS', true);
45212                                 editor.deferFocus();
45213                             }else{
45214                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45215                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45216                                 editor.deferFocus();
45217                             }
45218                         },
45219                         scope:editorcore,
45220                         clickEvent:'mousedown'
45221                     })
45222                 }
45223             );
45224         };
45225         // now add all the items...
45226         
45227
45228         if(!this.disable.alignments){
45229             tb.add(
45230                 '-',
45231                 btn('justifyleft'),
45232                 btn('justifycenter'),
45233                 btn('justifyright')
45234             );
45235         };
45236
45237         //if(!Roo.isSafari){
45238             if(!this.disable.links){
45239                 tb.add(
45240                     '-',
45241                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45242                 );
45243             };
45244
45245             if(!this.disable.lists){
45246                 tb.add(
45247                     '-',
45248                     btn('insertorderedlist'),
45249                     btn('insertunorderedlist')
45250                 );
45251             }
45252             if(!this.disable.sourceEdit){
45253                 tb.add(
45254                     '-',
45255                     btn('sourceedit', true, function(btn){
45256                         this.toggleSourceEdit(btn.pressed);
45257                     })
45258                 );
45259             }
45260         //}
45261         
45262         var smenu = { };
45263         // special menu.. - needs to be tidied up..
45264         if (!this.disable.special) {
45265             smenu = {
45266                 text: "&#169;",
45267                 cls: 'x-edit-none',
45268                 
45269                 menu : {
45270                     items : []
45271                 }
45272             };
45273             for (var i =0; i < this.specialChars.length; i++) {
45274                 smenu.menu.items.push({
45275                     
45276                     html: this.specialChars[i],
45277                     handler: function(a,b) {
45278                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45279                         //editor.insertAtCursor(a.html);
45280                         
45281                     },
45282                     tabIndex:-1
45283                 });
45284             }
45285             
45286             
45287             tb.add(smenu);
45288             
45289             
45290         }
45291         
45292         var cmenu = { };
45293         if (!this.disable.cleanStyles) {
45294             cmenu = {
45295                 cls: 'x-btn-icon x-btn-clear',
45296                 
45297                 menu : {
45298                     items : []
45299                 }
45300             };
45301             for (var i =0; i < this.cleanStyles.length; i++) {
45302                 cmenu.menu.items.push({
45303                     actiontype : this.cleanStyles[i],
45304                     html: 'Remove ' + this.cleanStyles[i],
45305                     handler: function(a,b) {
45306 //                        Roo.log(a);
45307 //                        Roo.log(b);
45308                         var c = Roo.get(editorcore.doc.body);
45309                         c.select('[style]').each(function(s) {
45310                             s.dom.style.removeProperty(a.actiontype);
45311                         });
45312                         editorcore.syncValue();
45313                     },
45314                     tabIndex:-1
45315                 });
45316             }
45317              cmenu.menu.items.push({
45318                 actiontype : 'tablewidths',
45319                 html: 'Remove Table Widths',
45320                 handler: function(a,b) {
45321                     editorcore.cleanTableWidths();
45322                     editorcore.syncValue();
45323                 },
45324                 tabIndex:-1
45325             });
45326             cmenu.menu.items.push({
45327                 actiontype : 'word',
45328                 html: 'Remove MS Word Formating',
45329                 handler: function(a,b) {
45330                     editorcore.cleanWord();
45331                     editorcore.syncValue();
45332                 },
45333                 tabIndex:-1
45334             });
45335             
45336             cmenu.menu.items.push({
45337                 actiontype : 'all',
45338                 html: 'Remove All Styles',
45339                 handler: function(a,b) {
45340                     
45341                     var c = Roo.get(editorcore.doc.body);
45342                     c.select('[style]').each(function(s) {
45343                         s.dom.removeAttribute('style');
45344                     });
45345                     editorcore.syncValue();
45346                 },
45347                 tabIndex:-1
45348             });
45349             
45350             cmenu.menu.items.push({
45351                 actiontype : 'all',
45352                 html: 'Remove All CSS Classes',
45353                 handler: function(a,b) {
45354                     
45355                     var c = Roo.get(editorcore.doc.body);
45356                     c.select('[class]').each(function(s) {
45357                         s.dom.className = '';
45358                     });
45359                     editorcore.syncValue();
45360                 },
45361                 tabIndex:-1
45362             });
45363             
45364              cmenu.menu.items.push({
45365                 actiontype : 'tidy',
45366                 html: 'Tidy HTML Source',
45367                 handler: function(a,b) {
45368                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45369                     editorcore.syncValue();
45370                 },
45371                 tabIndex:-1
45372             });
45373             
45374             
45375             tb.add(cmenu);
45376         }
45377          
45378         if (!this.disable.specialElements) {
45379             var semenu = {
45380                 text: "Other;",
45381                 cls: 'x-edit-none',
45382                 menu : {
45383                     items : []
45384                 }
45385             };
45386             for (var i =0; i < this.specialElements.length; i++) {
45387                 semenu.menu.items.push(
45388                     Roo.apply({ 
45389                         handler: function(a,b) {
45390                             editor.insertAtCursor(this.ihtml);
45391                         }
45392                     }, this.specialElements[i])
45393                 );
45394                     
45395             }
45396             
45397             tb.add(semenu);
45398             
45399             
45400         }
45401          
45402         
45403         if (this.btns) {
45404             for(var i =0; i< this.btns.length;i++) {
45405                 var b = Roo.factory(this.btns[i],Roo.form);
45406                 b.cls =  'x-edit-none';
45407                 
45408                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45409                     b.cls += ' x-init-enable';
45410                 }
45411                 
45412                 b.scope = editorcore;
45413                 tb.add(b);
45414             }
45415         
45416         }
45417         
45418         
45419         
45420         // disable everything...
45421         
45422         this.tb.items.each(function(item){
45423             
45424            if(
45425                 item.id != editorcore.frameId+ '-sourceedit' && 
45426                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45427             ){
45428                 
45429                 item.disable();
45430             }
45431         });
45432         this.rendered = true;
45433         
45434         // the all the btns;
45435         editor.on('editorevent', this.updateToolbar, this);
45436         // other toolbars need to implement this..
45437         //editor.on('editmodechange', this.updateToolbar, this);
45438     },
45439     
45440     
45441     relayBtnCmd : function(btn) {
45442         this.editorcore.relayCmd(btn.cmd);
45443     },
45444     // private used internally
45445     createLink : function(){
45446         Roo.log("create link?");
45447         var url = prompt(this.createLinkText, this.defaultLinkValue);
45448         if(url && url != 'http:/'+'/'){
45449             this.editorcore.relayCmd('createlink', url);
45450         }
45451     },
45452
45453     
45454     /**
45455      * Protected method that will not generally be called directly. It triggers
45456      * a toolbar update by reading the markup state of the current selection in the editor.
45457      */
45458     updateToolbar: function(){
45459
45460         if(!this.editorcore.activated){
45461             this.editor.onFirstFocus();
45462             return;
45463         }
45464
45465         var btns = this.tb.items.map, 
45466             doc = this.editorcore.doc,
45467             frameId = this.editorcore.frameId;
45468
45469         if(!this.disable.font && !Roo.isSafari){
45470             /*
45471             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45472             if(name != this.fontSelect.dom.value){
45473                 this.fontSelect.dom.value = name;
45474             }
45475             */
45476         }
45477         if(!this.disable.format){
45478             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45479             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45480             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45481             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45482         }
45483         if(!this.disable.alignments){
45484             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45485             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45486             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45487         }
45488         if(!Roo.isSafari && !this.disable.lists){
45489             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45490             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45491         }
45492         
45493         var ans = this.editorcore.getAllAncestors();
45494         if (this.formatCombo) {
45495             
45496             
45497             var store = this.formatCombo.store;
45498             this.formatCombo.setValue("");
45499             for (var i =0; i < ans.length;i++) {
45500                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45501                     // select it..
45502                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45503                     break;
45504                 }
45505             }
45506         }
45507         
45508         
45509         
45510         // hides menus... - so this cant be on a menu...
45511         Roo.menu.MenuMgr.hideAll();
45512
45513         //this.editorsyncValue();
45514     },
45515    
45516     
45517     createFontOptions : function(){
45518         var buf = [], fs = this.fontFamilies, ff, lc;
45519         
45520         
45521         
45522         for(var i = 0, len = fs.length; i< len; i++){
45523             ff = fs[i];
45524             lc = ff.toLowerCase();
45525             buf.push(
45526                 '<option value="',lc,'" style="font-family:',ff,';"',
45527                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45528                     ff,
45529                 '</option>'
45530             );
45531         }
45532         return buf.join('');
45533     },
45534     
45535     toggleSourceEdit : function(sourceEditMode){
45536         
45537         Roo.log("toolbar toogle");
45538         if(sourceEditMode === undefined){
45539             sourceEditMode = !this.sourceEditMode;
45540         }
45541         this.sourceEditMode = sourceEditMode === true;
45542         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45543         // just toggle the button?
45544         if(btn.pressed !== this.sourceEditMode){
45545             btn.toggle(this.sourceEditMode);
45546             return;
45547         }
45548         
45549         if(sourceEditMode){
45550             Roo.log("disabling buttons");
45551             this.tb.items.each(function(item){
45552                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45553                     item.disable();
45554                 }
45555             });
45556           
45557         }else{
45558             Roo.log("enabling buttons");
45559             if(this.editorcore.initialized){
45560                 this.tb.items.each(function(item){
45561                     item.enable();
45562                 });
45563             }
45564             
45565         }
45566         Roo.log("calling toggole on editor");
45567         // tell the editor that it's been pressed..
45568         this.editor.toggleSourceEdit(sourceEditMode);
45569        
45570     },
45571      /**
45572      * Object collection of toolbar tooltips for the buttons in the editor. The key
45573      * is the command id associated with that button and the value is a valid QuickTips object.
45574      * For example:
45575 <pre><code>
45576 {
45577     bold : {
45578         title: 'Bold (Ctrl+B)',
45579         text: 'Make the selected text bold.',
45580         cls: 'x-html-editor-tip'
45581     },
45582     italic : {
45583         title: 'Italic (Ctrl+I)',
45584         text: 'Make the selected text italic.',
45585         cls: 'x-html-editor-tip'
45586     },
45587     ...
45588 </code></pre>
45589     * @type Object
45590      */
45591     buttonTips : {
45592         bold : {
45593             title: 'Bold (Ctrl+B)',
45594             text: 'Make the selected text bold.',
45595             cls: 'x-html-editor-tip'
45596         },
45597         italic : {
45598             title: 'Italic (Ctrl+I)',
45599             text: 'Make the selected text italic.',
45600             cls: 'x-html-editor-tip'
45601         },
45602         underline : {
45603             title: 'Underline (Ctrl+U)',
45604             text: 'Underline the selected text.',
45605             cls: 'x-html-editor-tip'
45606         },
45607         strikethrough : {
45608             title: 'Strikethrough',
45609             text: 'Strikethrough the selected text.',
45610             cls: 'x-html-editor-tip'
45611         },
45612         increasefontsize : {
45613             title: 'Grow Text',
45614             text: 'Increase the font size.',
45615             cls: 'x-html-editor-tip'
45616         },
45617         decreasefontsize : {
45618             title: 'Shrink Text',
45619             text: 'Decrease the font size.',
45620             cls: 'x-html-editor-tip'
45621         },
45622         backcolor : {
45623             title: 'Text Highlight Color',
45624             text: 'Change the background color of the selected text.',
45625             cls: 'x-html-editor-tip'
45626         },
45627         forecolor : {
45628             title: 'Font Color',
45629             text: 'Change the color of the selected text.',
45630             cls: 'x-html-editor-tip'
45631         },
45632         justifyleft : {
45633             title: 'Align Text Left',
45634             text: 'Align text to the left.',
45635             cls: 'x-html-editor-tip'
45636         },
45637         justifycenter : {
45638             title: 'Center Text',
45639             text: 'Center text in the editor.',
45640             cls: 'x-html-editor-tip'
45641         },
45642         justifyright : {
45643             title: 'Align Text Right',
45644             text: 'Align text to the right.',
45645             cls: 'x-html-editor-tip'
45646         },
45647         insertunorderedlist : {
45648             title: 'Bullet List',
45649             text: 'Start a bulleted list.',
45650             cls: 'x-html-editor-tip'
45651         },
45652         insertorderedlist : {
45653             title: 'Numbered List',
45654             text: 'Start a numbered list.',
45655             cls: 'x-html-editor-tip'
45656         },
45657         createlink : {
45658             title: 'Hyperlink',
45659             text: 'Make the selected text a hyperlink.',
45660             cls: 'x-html-editor-tip'
45661         },
45662         sourceedit : {
45663             title: 'Source Edit',
45664             text: 'Switch to source editing mode.',
45665             cls: 'x-html-editor-tip'
45666         }
45667     },
45668     // private
45669     onDestroy : function(){
45670         if(this.rendered){
45671             
45672             this.tb.items.each(function(item){
45673                 if(item.menu){
45674                     item.menu.removeAll();
45675                     if(item.menu.el){
45676                         item.menu.el.destroy();
45677                     }
45678                 }
45679                 item.destroy();
45680             });
45681              
45682         }
45683     },
45684     onFirstFocus: function() {
45685         this.tb.items.each(function(item){
45686            item.enable();
45687         });
45688     }
45689 });
45690
45691
45692
45693
45694 // <script type="text/javascript">
45695 /*
45696  * Based on
45697  * Ext JS Library 1.1.1
45698  * Copyright(c) 2006-2007, Ext JS, LLC.
45699  *  
45700  
45701  */
45702
45703  
45704 /**
45705  * @class Roo.form.HtmlEditor.ToolbarContext
45706  * Context Toolbar
45707  * 
45708  * Usage:
45709  *
45710  new Roo.form.HtmlEditor({
45711     ....
45712     toolbars : [
45713         { xtype: 'ToolbarStandard', styles : {} }
45714         { xtype: 'ToolbarContext', disable : {} }
45715     ]
45716 })
45717
45718      
45719  * 
45720  * @config : {Object} disable List of elements to disable.. (not done yet.)
45721  * @config : {Object} styles  Map of styles available.
45722  * 
45723  */
45724
45725 Roo.form.HtmlEditor.ToolbarContext = function(config)
45726 {
45727     
45728     Roo.apply(this, config);
45729     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45730     // dont call parent... till later.
45731     this.styles = this.styles || {};
45732 }
45733
45734  
45735
45736 Roo.form.HtmlEditor.ToolbarContext.types = {
45737     'IMG' : {
45738         width : {
45739             title: "Width",
45740             width: 40
45741         },
45742         height:  {
45743             title: "Height",
45744             width: 40
45745         },
45746         align: {
45747             title: "Align",
45748             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45749             width : 80
45750             
45751         },
45752         border: {
45753             title: "Border",
45754             width: 40
45755         },
45756         alt: {
45757             title: "Alt",
45758             width: 120
45759         },
45760         src : {
45761             title: "Src",
45762             width: 220
45763         }
45764         
45765     },
45766     'A' : {
45767         name : {
45768             title: "Name",
45769             width: 50
45770         },
45771         target:  {
45772             title: "Target",
45773             width: 120
45774         },
45775         href:  {
45776             title: "Href",
45777             width: 220
45778         } // border?
45779         
45780     },
45781     'TABLE' : {
45782         rows : {
45783             title: "Rows",
45784             width: 20
45785         },
45786         cols : {
45787             title: "Cols",
45788             width: 20
45789         },
45790         width : {
45791             title: "Width",
45792             width: 40
45793         },
45794         height : {
45795             title: "Height",
45796             width: 40
45797         },
45798         border : {
45799             title: "Border",
45800             width: 20
45801         }
45802     },
45803     'TD' : {
45804         width : {
45805             title: "Width",
45806             width: 40
45807         },
45808         height : {
45809             title: "Height",
45810             width: 40
45811         },   
45812         align: {
45813             title: "Align",
45814             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45815             width: 80
45816         },
45817         valign: {
45818             title: "Valign",
45819             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45820             width: 80
45821         },
45822         colspan: {
45823             title: "Colspan",
45824             width: 20
45825             
45826         },
45827          'font-family'  : {
45828             title : "Font",
45829             style : 'fontFamily',
45830             displayField: 'display',
45831             optname : 'font-family',
45832             width: 140
45833         }
45834     },
45835     'INPUT' : {
45836         name : {
45837             title: "name",
45838             width: 120
45839         },
45840         value : {
45841             title: "Value",
45842             width: 120
45843         },
45844         width : {
45845             title: "Width",
45846             width: 40
45847         }
45848     },
45849     'LABEL' : {
45850         'for' : {
45851             title: "For",
45852             width: 120
45853         }
45854     },
45855     'TEXTAREA' : {
45856           name : {
45857             title: "name",
45858             width: 120
45859         },
45860         rows : {
45861             title: "Rows",
45862             width: 20
45863         },
45864         cols : {
45865             title: "Cols",
45866             width: 20
45867         }
45868     },
45869     'SELECT' : {
45870         name : {
45871             title: "name",
45872             width: 120
45873         },
45874         selectoptions : {
45875             title: "Options",
45876             width: 200
45877         }
45878     },
45879     
45880     // should we really allow this??
45881     // should this just be 
45882     'BODY' : {
45883         title : {
45884             title: "Title",
45885             width: 200,
45886             disabled : true
45887         }
45888     },
45889     'SPAN' : {
45890         'font-family'  : {
45891             title : "Font",
45892             style : 'fontFamily',
45893             displayField: 'display',
45894             optname : 'font-family',
45895             width: 140
45896         }
45897     },
45898     'DIV' : {
45899         'font-family'  : {
45900             title : "Font",
45901             style : 'fontFamily',
45902             displayField: 'display',
45903             optname : 'font-family',
45904             width: 140
45905         }
45906     },
45907      'P' : {
45908         'font-family'  : {
45909             title : "Font",
45910             style : 'fontFamily',
45911             displayField: 'display',
45912             optname : 'font-family',
45913             width: 140
45914         }
45915     },
45916     
45917     '*' : {
45918         // empty..
45919     }
45920
45921 };
45922
45923 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45924 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45925
45926 Roo.form.HtmlEditor.ToolbarContext.options = {
45927         'font-family'  : [ 
45928                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45929                 [ 'Courier New', 'Courier New'],
45930                 [ 'Tahoma', 'Tahoma'],
45931                 [ 'Times New Roman,serif', 'Times'],
45932                 [ 'Verdana','Verdana' ]
45933         ]
45934 };
45935
45936 // fixme - these need to be configurable..
45937  
45938
45939 //Roo.form.HtmlEditor.ToolbarContext.types
45940
45941
45942 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45943     
45944     tb: false,
45945     
45946     rendered: false,
45947     
45948     editor : false,
45949     editorcore : false,
45950     /**
45951      * @cfg {Object} disable  List of toolbar elements to disable
45952          
45953      */
45954     disable : false,
45955     /**
45956      * @cfg {Object} styles List of styles 
45957      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
45958      *
45959      * These must be defined in the page, so they get rendered correctly..
45960      * .headline { }
45961      * TD.underline { }
45962      * 
45963      */
45964     styles : false,
45965     
45966     options: false,
45967     
45968     toolbars : false,
45969     
45970     init : function(editor)
45971     {
45972         this.editor = editor;
45973         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45974         var editorcore = this.editorcore;
45975         
45976         var fid = editorcore.frameId;
45977         var etb = this;
45978         function btn(id, toggle, handler){
45979             var xid = fid + '-'+ id ;
45980             return {
45981                 id : xid,
45982                 cmd : id,
45983                 cls : 'x-btn-icon x-edit-'+id,
45984                 enableToggle:toggle !== false,
45985                 scope: editorcore, // was editor...
45986                 handler:handler||editorcore.relayBtnCmd,
45987                 clickEvent:'mousedown',
45988                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45989                 tabIndex:-1
45990             };
45991         }
45992         // create a new element.
45993         var wdiv = editor.wrap.createChild({
45994                 tag: 'div'
45995             }, editor.wrap.dom.firstChild.nextSibling, true);
45996         
45997         // can we do this more than once??
45998         
45999          // stop form submits
46000       
46001  
46002         // disable everything...
46003         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46004         this.toolbars = {};
46005            
46006         for (var i in  ty) {
46007           
46008             this.toolbars[i] = this.buildToolbar(ty[i],i);
46009         }
46010         this.tb = this.toolbars.BODY;
46011         this.tb.el.show();
46012         this.buildFooter();
46013         this.footer.show();
46014         editor.on('hide', function( ) { this.footer.hide() }, this);
46015         editor.on('show', function( ) { this.footer.show() }, this);
46016         
46017          
46018         this.rendered = true;
46019         
46020         // the all the btns;
46021         editor.on('editorevent', this.updateToolbar, this);
46022         // other toolbars need to implement this..
46023         //editor.on('editmodechange', this.updateToolbar, this);
46024     },
46025     
46026     
46027     
46028     /**
46029      * Protected method that will not generally be called directly. It triggers
46030      * a toolbar update by reading the markup state of the current selection in the editor.
46031      *
46032      * Note you can force an update by calling on('editorevent', scope, false)
46033      */
46034     updateToolbar: function(editor,ev,sel){
46035
46036         //Roo.log(ev);
46037         // capture mouse up - this is handy for selecting images..
46038         // perhaps should go somewhere else...
46039         if(!this.editorcore.activated){
46040              this.editor.onFirstFocus();
46041             return;
46042         }
46043         
46044         
46045         
46046         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46047         // selectNode - might want to handle IE?
46048         if (ev &&
46049             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46050             ev.target && ev.target.tagName == 'IMG') {
46051             // they have click on an image...
46052             // let's see if we can change the selection...
46053             sel = ev.target;
46054          
46055               var nodeRange = sel.ownerDocument.createRange();
46056             try {
46057                 nodeRange.selectNode(sel);
46058             } catch (e) {
46059                 nodeRange.selectNodeContents(sel);
46060             }
46061             //nodeRange.collapse(true);
46062             var s = this.editorcore.win.getSelection();
46063             s.removeAllRanges();
46064             s.addRange(nodeRange);
46065         }  
46066         
46067       
46068         var updateFooter = sel ? false : true;
46069         
46070         
46071         var ans = this.editorcore.getAllAncestors();
46072         
46073         // pick
46074         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46075         
46076         if (!sel) { 
46077             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46078             sel = sel ? sel : this.editorcore.doc.body;
46079             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46080             
46081         }
46082         // pick a menu that exists..
46083         var tn = sel.tagName.toUpperCase();
46084         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46085         
46086         tn = sel.tagName.toUpperCase();
46087         
46088         var lastSel = this.tb.selectedNode;
46089         
46090         this.tb.selectedNode = sel;
46091         
46092         // if current menu does not match..
46093         
46094         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46095                 
46096             this.tb.el.hide();
46097             ///console.log("show: " + tn);
46098             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46099             this.tb.el.show();
46100             // update name
46101             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46102             
46103             
46104             // update attributes
46105             if (this.tb.fields) {
46106                 this.tb.fields.each(function(e) {
46107                     if (e.stylename) {
46108                         e.setValue(sel.style[e.stylename]);
46109                         return;
46110                     } 
46111                    e.setValue(sel.getAttribute(e.attrname));
46112                 });
46113             }
46114             
46115             var hasStyles = false;
46116             for(var i in this.styles) {
46117                 hasStyles = true;
46118                 break;
46119             }
46120             
46121             // update styles
46122             if (hasStyles) { 
46123                 var st = this.tb.fields.item(0);
46124                 
46125                 st.store.removeAll();
46126                
46127                 
46128                 var cn = sel.className.split(/\s+/);
46129                 
46130                 var avs = [];
46131                 if (this.styles['*']) {
46132                     
46133                     Roo.each(this.styles['*'], function(v) {
46134                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46135                     });
46136                 }
46137                 if (this.styles[tn]) { 
46138                     Roo.each(this.styles[tn], function(v) {
46139                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46140                     });
46141                 }
46142                 
46143                 st.store.loadData(avs);
46144                 st.collapse();
46145                 st.setValue(cn);
46146             }
46147             // flag our selected Node.
46148             this.tb.selectedNode = sel;
46149            
46150            
46151             Roo.menu.MenuMgr.hideAll();
46152
46153         }
46154         
46155         if (!updateFooter) {
46156             //this.footDisp.dom.innerHTML = ''; 
46157             return;
46158         }
46159         // update the footer
46160         //
46161         var html = '';
46162         
46163         this.footerEls = ans.reverse();
46164         Roo.each(this.footerEls, function(a,i) {
46165             if (!a) { return; }
46166             html += html.length ? ' &gt; '  :  '';
46167             
46168             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46169             
46170         });
46171        
46172         // 
46173         var sz = this.footDisp.up('td').getSize();
46174         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46175         this.footDisp.dom.style.marginLeft = '5px';
46176         
46177         this.footDisp.dom.style.overflow = 'hidden';
46178         
46179         this.footDisp.dom.innerHTML = html;
46180             
46181         //this.editorsyncValue();
46182     },
46183      
46184     
46185    
46186        
46187     // private
46188     onDestroy : function(){
46189         if(this.rendered){
46190             
46191             this.tb.items.each(function(item){
46192                 if(item.menu){
46193                     item.menu.removeAll();
46194                     if(item.menu.el){
46195                         item.menu.el.destroy();
46196                     }
46197                 }
46198                 item.destroy();
46199             });
46200              
46201         }
46202     },
46203     onFirstFocus: function() {
46204         // need to do this for all the toolbars..
46205         this.tb.items.each(function(item){
46206            item.enable();
46207         });
46208     },
46209     buildToolbar: function(tlist, nm)
46210     {
46211         var editor = this.editor;
46212         var editorcore = this.editorcore;
46213          // create a new element.
46214         var wdiv = editor.wrap.createChild({
46215                 tag: 'div'
46216             }, editor.wrap.dom.firstChild.nextSibling, true);
46217         
46218        
46219         var tb = new Roo.Toolbar(wdiv);
46220         // add the name..
46221         
46222         tb.add(nm+ ":&nbsp;");
46223         
46224         var styles = [];
46225         for(var i in this.styles) {
46226             styles.push(i);
46227         }
46228         
46229         // styles...
46230         if (styles && styles.length) {
46231             
46232             // this needs a multi-select checkbox...
46233             tb.addField( new Roo.form.ComboBox({
46234                 store: new Roo.data.SimpleStore({
46235                     id : 'val',
46236                     fields: ['val', 'selected'],
46237                     data : [] 
46238                 }),
46239                 name : '-roo-edit-className',
46240                 attrname : 'className',
46241                 displayField: 'val',
46242                 typeAhead: false,
46243                 mode: 'local',
46244                 editable : false,
46245                 triggerAction: 'all',
46246                 emptyText:'Select Style',
46247                 selectOnFocus:true,
46248                 width: 130,
46249                 listeners : {
46250                     'select': function(c, r, i) {
46251                         // initial support only for on class per el..
46252                         tb.selectedNode.className =  r ? r.get('val') : '';
46253                         editorcore.syncValue();
46254                     }
46255                 }
46256     
46257             }));
46258         }
46259         
46260         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46261         var tbops = tbc.options;
46262         
46263         for (var i in tlist) {
46264             
46265             var item = tlist[i];
46266             tb.add(item.title + ":&nbsp;");
46267             
46268             
46269             //optname == used so you can configure the options available..
46270             var opts = item.opts ? item.opts : false;
46271             if (item.optname) {
46272                 opts = tbops[item.optname];
46273            
46274             }
46275             
46276             if (opts) {
46277                 // opts == pulldown..
46278                 tb.addField( new Roo.form.ComboBox({
46279                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46280                         id : 'val',
46281                         fields: ['val', 'display'],
46282                         data : opts  
46283                     }),
46284                     name : '-roo-edit-' + i,
46285                     attrname : i,
46286                     stylename : item.style ? item.style : false,
46287                     displayField: item.displayField ? item.displayField : 'val',
46288                     valueField :  'val',
46289                     typeAhead: false,
46290                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46291                     editable : false,
46292                     triggerAction: 'all',
46293                     emptyText:'Select',
46294                     selectOnFocus:true,
46295                     width: item.width ? item.width  : 130,
46296                     listeners : {
46297                         'select': function(c, r, i) {
46298                             if (c.stylename) {
46299                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46300                                 return;
46301                             }
46302                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46303                         }
46304                     }
46305
46306                 }));
46307                 continue;
46308                     
46309                  
46310                 
46311                 tb.addField( new Roo.form.TextField({
46312                     name: i,
46313                     width: 100,
46314                     //allowBlank:false,
46315                     value: ''
46316                 }));
46317                 continue;
46318             }
46319             tb.addField( new Roo.form.TextField({
46320                 name: '-roo-edit-' + i,
46321                 attrname : i,
46322                 
46323                 width: item.width,
46324                 //allowBlank:true,
46325                 value: '',
46326                 listeners: {
46327                     'change' : function(f, nv, ov) {
46328                         tb.selectedNode.setAttribute(f.attrname, nv);
46329                         editorcore.syncValue();
46330                     }
46331                 }
46332             }));
46333              
46334         }
46335         
46336         var _this = this;
46337         
46338         if(nm == 'BODY'){
46339             tb.addSeparator();
46340         
46341             tb.addButton( {
46342                 text: 'Stylesheets',
46343
46344                 listeners : {
46345                     click : function ()
46346                     {
46347                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46348                     }
46349                 }
46350             });
46351         }
46352         
46353         tb.addFill();
46354         tb.addButton( {
46355             text: 'Remove Tag',
46356     
46357             listeners : {
46358                 click : function ()
46359                 {
46360                     // remove
46361                     // undo does not work.
46362                      
46363                     var sn = tb.selectedNode;
46364                     
46365                     var pn = sn.parentNode;
46366                     
46367                     var stn =  sn.childNodes[0];
46368                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46369                     while (sn.childNodes.length) {
46370                         var node = sn.childNodes[0];
46371                         sn.removeChild(node);
46372                         //Roo.log(node);
46373                         pn.insertBefore(node, sn);
46374                         
46375                     }
46376                     pn.removeChild(sn);
46377                     var range = editorcore.createRange();
46378         
46379                     range.setStart(stn,0);
46380                     range.setEnd(en,0); //????
46381                     //range.selectNode(sel);
46382                     
46383                     
46384                     var selection = editorcore.getSelection();
46385                     selection.removeAllRanges();
46386                     selection.addRange(range);
46387                     
46388                     
46389                     
46390                     //_this.updateToolbar(null, null, pn);
46391                     _this.updateToolbar(null, null, null);
46392                     _this.footDisp.dom.innerHTML = ''; 
46393                 }
46394             }
46395             
46396                     
46397                 
46398             
46399         });
46400         
46401         
46402         tb.el.on('click', function(e){
46403             e.preventDefault(); // what does this do?
46404         });
46405         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46406         tb.el.hide();
46407         tb.name = nm;
46408         // dont need to disable them... as they will get hidden
46409         return tb;
46410          
46411         
46412     },
46413     buildFooter : function()
46414     {
46415         
46416         var fel = this.editor.wrap.createChild();
46417         this.footer = new Roo.Toolbar(fel);
46418         // toolbar has scrolly on left / right?
46419         var footDisp= new Roo.Toolbar.Fill();
46420         var _t = this;
46421         this.footer.add(
46422             {
46423                 text : '&lt;',
46424                 xtype: 'Button',
46425                 handler : function() {
46426                     _t.footDisp.scrollTo('left',0,true)
46427                 }
46428             }
46429         );
46430         this.footer.add( footDisp );
46431         this.footer.add( 
46432             {
46433                 text : '&gt;',
46434                 xtype: 'Button',
46435                 handler : function() {
46436                     // no animation..
46437                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46438                 }
46439             }
46440         );
46441         var fel = Roo.get(footDisp.el);
46442         fel.addClass('x-editor-context');
46443         this.footDispWrap = fel; 
46444         this.footDispWrap.overflow  = 'hidden';
46445         
46446         this.footDisp = fel.createChild();
46447         this.footDispWrap.on('click', this.onContextClick, this)
46448         
46449         
46450     },
46451     onContextClick : function (ev,dom)
46452     {
46453         ev.preventDefault();
46454         var  cn = dom.className;
46455         //Roo.log(cn);
46456         if (!cn.match(/x-ed-loc-/)) {
46457             return;
46458         }
46459         var n = cn.split('-').pop();
46460         var ans = this.footerEls;
46461         var sel = ans[n];
46462         
46463          // pick
46464         var range = this.editorcore.createRange();
46465         
46466         range.selectNodeContents(sel);
46467         //range.selectNode(sel);
46468         
46469         
46470         var selection = this.editorcore.getSelection();
46471         selection.removeAllRanges();
46472         selection.addRange(range);
46473         
46474         
46475         
46476         this.updateToolbar(null, null, sel);
46477         
46478         
46479     }
46480     
46481     
46482     
46483     
46484     
46485 });
46486
46487
46488
46489
46490
46491 /*
46492  * Based on:
46493  * Ext JS Library 1.1.1
46494  * Copyright(c) 2006-2007, Ext JS, LLC.
46495  *
46496  * Originally Released Under LGPL - original licence link has changed is not relivant.
46497  *
46498  * Fork - LGPL
46499  * <script type="text/javascript">
46500  */
46501  
46502 /**
46503  * @class Roo.form.BasicForm
46504  * @extends Roo.util.Observable
46505  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46506  * @constructor
46507  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46508  * @param {Object} config Configuration options
46509  */
46510 Roo.form.BasicForm = function(el, config){
46511     this.allItems = [];
46512     this.childForms = [];
46513     Roo.apply(this, config);
46514     /*
46515      * The Roo.form.Field items in this form.
46516      * @type MixedCollection
46517      */
46518      
46519      
46520     this.items = new Roo.util.MixedCollection(false, function(o){
46521         return o.id || (o.id = Roo.id());
46522     });
46523     this.addEvents({
46524         /**
46525          * @event beforeaction
46526          * Fires before any action is performed. Return false to cancel the action.
46527          * @param {Form} this
46528          * @param {Action} action The action to be performed
46529          */
46530         beforeaction: true,
46531         /**
46532          * @event actionfailed
46533          * Fires when an action fails.
46534          * @param {Form} this
46535          * @param {Action} action The action that failed
46536          */
46537         actionfailed : true,
46538         /**
46539          * @event actioncomplete
46540          * Fires when an action is completed.
46541          * @param {Form} this
46542          * @param {Action} action The action that completed
46543          */
46544         actioncomplete : true
46545     });
46546     if(el){
46547         this.initEl(el);
46548     }
46549     Roo.form.BasicForm.superclass.constructor.call(this);
46550 };
46551
46552 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46553     /**
46554      * @cfg {String} method
46555      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46556      */
46557     /**
46558      * @cfg {DataReader} reader
46559      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46560      * This is optional as there is built-in support for processing JSON.
46561      */
46562     /**
46563      * @cfg {DataReader} errorReader
46564      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46565      * This is completely optional as there is built-in support for processing JSON.
46566      */
46567     /**
46568      * @cfg {String} url
46569      * The URL to use for form actions if one isn't supplied in the action options.
46570      */
46571     /**
46572      * @cfg {Boolean} fileUpload
46573      * Set to true if this form is a file upload.
46574      */
46575      
46576     /**
46577      * @cfg {Object} baseParams
46578      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46579      */
46580      /**
46581      
46582     /**
46583      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46584      */
46585     timeout: 30,
46586
46587     // private
46588     activeAction : null,
46589
46590     /**
46591      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46592      * or setValues() data instead of when the form was first created.
46593      */
46594     trackResetOnLoad : false,
46595     
46596     
46597     /**
46598      * childForms - used for multi-tab forms
46599      * @type {Array}
46600      */
46601     childForms : false,
46602     
46603     /**
46604      * allItems - full list of fields.
46605      * @type {Array}
46606      */
46607     allItems : false,
46608     
46609     /**
46610      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46611      * element by passing it or its id or mask the form itself by passing in true.
46612      * @type Mixed
46613      */
46614     waitMsgTarget : false,
46615
46616     // private
46617     initEl : function(el){
46618         this.el = Roo.get(el);
46619         this.id = this.el.id || Roo.id();
46620         this.el.on('submit', this.onSubmit, this);
46621         this.el.addClass('x-form');
46622     },
46623
46624     // private
46625     onSubmit : function(e){
46626         e.stopEvent();
46627     },
46628
46629     /**
46630      * Returns true if client-side validation on the form is successful.
46631      * @return Boolean
46632      */
46633     isValid : function(){
46634         var valid = true;
46635         this.items.each(function(f){
46636            if(!f.validate()){
46637                valid = false;
46638            }
46639         });
46640         return valid;
46641     },
46642
46643     /**
46644      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46645      * @return Boolean
46646      */
46647     isDirty : function(){
46648         var dirty = false;
46649         this.items.each(function(f){
46650            if(f.isDirty()){
46651                dirty = true;
46652                return false;
46653            }
46654         });
46655         return dirty;
46656     },
46657     
46658     /**
46659      * Returns true if any fields in this form have changed since their original load. (New version)
46660      * @return Boolean
46661      */
46662     
46663     hasChanged : function()
46664     {
46665         var dirty = false;
46666         this.items.each(function(f){
46667            if(f.hasChanged()){
46668                dirty = true;
46669                return false;
46670            }
46671         });
46672         return dirty;
46673         
46674     },
46675     /**
46676      * Resets all hasChanged to 'false' -
46677      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46678      * So hasChanged storage is only to be used for this purpose
46679      * @return Boolean
46680      */
46681     resetHasChanged : function()
46682     {
46683         this.items.each(function(f){
46684            f.resetHasChanged();
46685         });
46686         
46687     },
46688     
46689     
46690     /**
46691      * Performs a predefined action (submit or load) or custom actions you define on this form.
46692      * @param {String} actionName The name of the action type
46693      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46694      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46695      * accept other config options):
46696      * <pre>
46697 Property          Type             Description
46698 ----------------  ---------------  ----------------------------------------------------------------------------------
46699 url               String           The url for the action (defaults to the form's url)
46700 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46701 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46702 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46703                                    validate the form on the client (defaults to false)
46704      * </pre>
46705      * @return {BasicForm} this
46706      */
46707     doAction : function(action, options){
46708         if(typeof action == 'string'){
46709             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46710         }
46711         if(this.fireEvent('beforeaction', this, action) !== false){
46712             this.beforeAction(action);
46713             action.run.defer(100, action);
46714         }
46715         return this;
46716     },
46717
46718     /**
46719      * Shortcut to do a submit action.
46720      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46721      * @return {BasicForm} this
46722      */
46723     submit : function(options){
46724         this.doAction('submit', options);
46725         return this;
46726     },
46727
46728     /**
46729      * Shortcut to do a load action.
46730      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46731      * @return {BasicForm} this
46732      */
46733     load : function(options){
46734         this.doAction('load', options);
46735         return this;
46736     },
46737
46738     /**
46739      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46740      * @param {Record} record The record to edit
46741      * @return {BasicForm} this
46742      */
46743     updateRecord : function(record){
46744         record.beginEdit();
46745         var fs = record.fields;
46746         fs.each(function(f){
46747             var field = this.findField(f.name);
46748             if(field){
46749                 record.set(f.name, field.getValue());
46750             }
46751         }, this);
46752         record.endEdit();
46753         return this;
46754     },
46755
46756     /**
46757      * Loads an Roo.data.Record into this form.
46758      * @param {Record} record The record to load
46759      * @return {BasicForm} this
46760      */
46761     loadRecord : function(record){
46762         this.setValues(record.data);
46763         return this;
46764     },
46765
46766     // private
46767     beforeAction : function(action){
46768         var o = action.options;
46769         
46770        
46771         if(this.waitMsgTarget === true){
46772             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46773         }else if(this.waitMsgTarget){
46774             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46775             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46776         }else {
46777             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46778         }
46779          
46780     },
46781
46782     // private
46783     afterAction : function(action, success){
46784         this.activeAction = null;
46785         var o = action.options;
46786         
46787         if(this.waitMsgTarget === true){
46788             this.el.unmask();
46789         }else if(this.waitMsgTarget){
46790             this.waitMsgTarget.unmask();
46791         }else{
46792             Roo.MessageBox.updateProgress(1);
46793             Roo.MessageBox.hide();
46794         }
46795          
46796         if(success){
46797             if(o.reset){
46798                 this.reset();
46799             }
46800             Roo.callback(o.success, o.scope, [this, action]);
46801             this.fireEvent('actioncomplete', this, action);
46802             
46803         }else{
46804             
46805             // failure condition..
46806             // we have a scenario where updates need confirming.
46807             // eg. if a locking scenario exists..
46808             // we look for { errors : { needs_confirm : true }} in the response.
46809             if (
46810                 (typeof(action.result) != 'undefined')  &&
46811                 (typeof(action.result.errors) != 'undefined')  &&
46812                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46813            ){
46814                 var _t = this;
46815                 Roo.MessageBox.confirm(
46816                     "Change requires confirmation",
46817                     action.result.errorMsg,
46818                     function(r) {
46819                         if (r != 'yes') {
46820                             return;
46821                         }
46822                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46823                     }
46824                     
46825                 );
46826                 
46827                 
46828                 
46829                 return;
46830             }
46831             
46832             Roo.callback(o.failure, o.scope, [this, action]);
46833             // show an error message if no failed handler is set..
46834             if (!this.hasListener('actionfailed')) {
46835                 Roo.MessageBox.alert("Error",
46836                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46837                         action.result.errorMsg :
46838                         "Saving Failed, please check your entries or try again"
46839                 );
46840             }
46841             
46842             this.fireEvent('actionfailed', this, action);
46843         }
46844         
46845     },
46846
46847     /**
46848      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46849      * @param {String} id The value to search for
46850      * @return Field
46851      */
46852     findField : function(id){
46853         var field = this.items.get(id);
46854         if(!field){
46855             this.items.each(function(f){
46856                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46857                     field = f;
46858                     return false;
46859                 }
46860             });
46861         }
46862         return field || null;
46863     },
46864
46865     /**
46866      * Add a secondary form to this one, 
46867      * Used to provide tabbed forms. One form is primary, with hidden values 
46868      * which mirror the elements from the other forms.
46869      * 
46870      * @param {Roo.form.Form} form to add.
46871      * 
46872      */
46873     addForm : function(form)
46874     {
46875        
46876         if (this.childForms.indexOf(form) > -1) {
46877             // already added..
46878             return;
46879         }
46880         this.childForms.push(form);
46881         var n = '';
46882         Roo.each(form.allItems, function (fe) {
46883             
46884             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46885             if (this.findField(n)) { // already added..
46886                 return;
46887             }
46888             var add = new Roo.form.Hidden({
46889                 name : n
46890             });
46891             add.render(this.el);
46892             
46893             this.add( add );
46894         }, this);
46895         
46896     },
46897     /**
46898      * Mark fields in this form invalid in bulk.
46899      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46900      * @return {BasicForm} this
46901      */
46902     markInvalid : function(errors){
46903         if(errors instanceof Array){
46904             for(var i = 0, len = errors.length; i < len; i++){
46905                 var fieldError = errors[i];
46906                 var f = this.findField(fieldError.id);
46907                 if(f){
46908                     f.markInvalid(fieldError.msg);
46909                 }
46910             }
46911         }else{
46912             var field, id;
46913             for(id in errors){
46914                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46915                     field.markInvalid(errors[id]);
46916                 }
46917             }
46918         }
46919         Roo.each(this.childForms || [], function (f) {
46920             f.markInvalid(errors);
46921         });
46922         
46923         return this;
46924     },
46925
46926     /**
46927      * Set values for fields in this form in bulk.
46928      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46929      * @return {BasicForm} this
46930      */
46931     setValues : function(values){
46932         if(values instanceof Array){ // array of objects
46933             for(var i = 0, len = values.length; i < len; i++){
46934                 var v = values[i];
46935                 var f = this.findField(v.id);
46936                 if(f){
46937                     f.setValue(v.value);
46938                     if(this.trackResetOnLoad){
46939                         f.originalValue = f.getValue();
46940                     }
46941                 }
46942             }
46943         }else{ // object hash
46944             var field, id;
46945             for(id in values){
46946                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46947                     
46948                     if (field.setFromData && 
46949                         field.valueField && 
46950                         field.displayField &&
46951                         // combos' with local stores can 
46952                         // be queried via setValue()
46953                         // to set their value..
46954                         (field.store && !field.store.isLocal)
46955                         ) {
46956                         // it's a combo
46957                         var sd = { };
46958                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
46959                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
46960                         field.setFromData(sd);
46961                         
46962                     } else {
46963                         field.setValue(values[id]);
46964                     }
46965                     
46966                     
46967                     if(this.trackResetOnLoad){
46968                         field.originalValue = field.getValue();
46969                     }
46970                 }
46971             }
46972         }
46973         this.resetHasChanged();
46974         
46975         
46976         Roo.each(this.childForms || [], function (f) {
46977             f.setValues(values);
46978             f.resetHasChanged();
46979         });
46980                 
46981         return this;
46982     },
46983
46984     /**
46985      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
46986      * they are returned as an array.
46987      * @param {Boolean} asString
46988      * @return {Object}
46989      */
46990     getValues : function(asString){
46991         if (this.childForms) {
46992             // copy values from the child forms
46993             Roo.each(this.childForms, function (f) {
46994                 this.setValues(f.getValues());
46995             }, this);
46996         }
46997         
46998         
46999         
47000         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47001         if(asString === true){
47002             return fs;
47003         }
47004         return Roo.urlDecode(fs);
47005     },
47006     
47007     /**
47008      * Returns the fields in this form as an object with key/value pairs. 
47009      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47010      * @return {Object}
47011      */
47012     getFieldValues : function(with_hidden)
47013     {
47014         if (this.childForms) {
47015             // copy values from the child forms
47016             // should this call getFieldValues - probably not as we do not currently copy
47017             // hidden fields when we generate..
47018             Roo.each(this.childForms, function (f) {
47019                 this.setValues(f.getValues());
47020             }, this);
47021         }
47022         
47023         var ret = {};
47024         this.items.each(function(f){
47025             if (!f.getName()) {
47026                 return;
47027             }
47028             var v = f.getValue();
47029             if (f.inputType =='radio') {
47030                 if (typeof(ret[f.getName()]) == 'undefined') {
47031                     ret[f.getName()] = ''; // empty..
47032                 }
47033                 
47034                 if (!f.el.dom.checked) {
47035                     return;
47036                     
47037                 }
47038                 v = f.el.dom.value;
47039                 
47040             }
47041             
47042             // not sure if this supported any more..
47043             if ((typeof(v) == 'object') && f.getRawValue) {
47044                 v = f.getRawValue() ; // dates..
47045             }
47046             // combo boxes where name != hiddenName...
47047             if (f.name != f.getName()) {
47048                 ret[f.name] = f.getRawValue();
47049             }
47050             ret[f.getName()] = v;
47051         });
47052         
47053         return ret;
47054     },
47055
47056     /**
47057      * Clears all invalid messages in this form.
47058      * @return {BasicForm} this
47059      */
47060     clearInvalid : function(){
47061         this.items.each(function(f){
47062            f.clearInvalid();
47063         });
47064         
47065         Roo.each(this.childForms || [], function (f) {
47066             f.clearInvalid();
47067         });
47068         
47069         
47070         return this;
47071     },
47072
47073     /**
47074      * Resets this form.
47075      * @return {BasicForm} this
47076      */
47077     reset : function(){
47078         this.items.each(function(f){
47079             f.reset();
47080         });
47081         
47082         Roo.each(this.childForms || [], function (f) {
47083             f.reset();
47084         });
47085         this.resetHasChanged();
47086         
47087         return this;
47088     },
47089
47090     /**
47091      * Add Roo.form components to this form.
47092      * @param {Field} field1
47093      * @param {Field} field2 (optional)
47094      * @param {Field} etc (optional)
47095      * @return {BasicForm} this
47096      */
47097     add : function(){
47098         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47099         return this;
47100     },
47101
47102
47103     /**
47104      * Removes a field from the items collection (does NOT remove its markup).
47105      * @param {Field} field
47106      * @return {BasicForm} this
47107      */
47108     remove : function(field){
47109         this.items.remove(field);
47110         return this;
47111     },
47112
47113     /**
47114      * Looks at the fields in this form, checks them for an id attribute,
47115      * and calls applyTo on the existing dom element with that id.
47116      * @return {BasicForm} this
47117      */
47118     render : function(){
47119         this.items.each(function(f){
47120             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47121                 f.applyTo(f.id);
47122             }
47123         });
47124         return this;
47125     },
47126
47127     /**
47128      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47129      * @param {Object} values
47130      * @return {BasicForm} this
47131      */
47132     applyToFields : function(o){
47133         this.items.each(function(f){
47134            Roo.apply(f, o);
47135         });
47136         return this;
47137     },
47138
47139     /**
47140      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47141      * @param {Object} values
47142      * @return {BasicForm} this
47143      */
47144     applyIfToFields : function(o){
47145         this.items.each(function(f){
47146            Roo.applyIf(f, o);
47147         });
47148         return this;
47149     }
47150 });
47151
47152 // back compat
47153 Roo.BasicForm = Roo.form.BasicForm;/*
47154  * Based on:
47155  * Ext JS Library 1.1.1
47156  * Copyright(c) 2006-2007, Ext JS, LLC.
47157  *
47158  * Originally Released Under LGPL - original licence link has changed is not relivant.
47159  *
47160  * Fork - LGPL
47161  * <script type="text/javascript">
47162  */
47163
47164 /**
47165  * @class Roo.form.Form
47166  * @extends Roo.form.BasicForm
47167  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47168  * @constructor
47169  * @param {Object} config Configuration options
47170  */
47171 Roo.form.Form = function(config){
47172     var xitems =  [];
47173     if (config.items) {
47174         xitems = config.items;
47175         delete config.items;
47176     }
47177    
47178     
47179     Roo.form.Form.superclass.constructor.call(this, null, config);
47180     this.url = this.url || this.action;
47181     if(!this.root){
47182         this.root = new Roo.form.Layout(Roo.applyIf({
47183             id: Roo.id()
47184         }, config));
47185     }
47186     this.active = this.root;
47187     /**
47188      * Array of all the buttons that have been added to this form via {@link addButton}
47189      * @type Array
47190      */
47191     this.buttons = [];
47192     this.allItems = [];
47193     this.addEvents({
47194         /**
47195          * @event clientvalidation
47196          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47197          * @param {Form} this
47198          * @param {Boolean} valid true if the form has passed client-side validation
47199          */
47200         clientvalidation: true,
47201         /**
47202          * @event rendered
47203          * Fires when the form is rendered
47204          * @param {Roo.form.Form} form
47205          */
47206         rendered : true
47207     });
47208     
47209     if (this.progressUrl) {
47210             // push a hidden field onto the list of fields..
47211             this.addxtype( {
47212                     xns: Roo.form, 
47213                     xtype : 'Hidden', 
47214                     name : 'UPLOAD_IDENTIFIER' 
47215             });
47216         }
47217         
47218     
47219     Roo.each(xitems, this.addxtype, this);
47220     
47221     
47222     
47223 };
47224
47225 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47226     /**
47227      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47228      */
47229     /**
47230      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47231      */
47232     /**
47233      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47234      */
47235     buttonAlign:'center',
47236
47237     /**
47238      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47239      */
47240     minButtonWidth:75,
47241
47242     /**
47243      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47244      * This property cascades to child containers if not set.
47245      */
47246     labelAlign:'left',
47247
47248     /**
47249      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47250      * fires a looping event with that state. This is required to bind buttons to the valid
47251      * state using the config value formBind:true on the button.
47252      */
47253     monitorValid : false,
47254
47255     /**
47256      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47257      */
47258     monitorPoll : 200,
47259     
47260     /**
47261      * @cfg {String} progressUrl - Url to return progress data 
47262      */
47263     
47264     progressUrl : false,
47265   
47266     /**
47267      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47268      * fields are added and the column is closed. If no fields are passed the column remains open
47269      * until end() is called.
47270      * @param {Object} config The config to pass to the column
47271      * @param {Field} field1 (optional)
47272      * @param {Field} field2 (optional)
47273      * @param {Field} etc (optional)
47274      * @return Column The column container object
47275      */
47276     column : function(c){
47277         var col = new Roo.form.Column(c);
47278         this.start(col);
47279         if(arguments.length > 1){ // duplicate code required because of Opera
47280             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47281             this.end();
47282         }
47283         return col;
47284     },
47285
47286     /**
47287      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47288      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47289      * until end() is called.
47290      * @param {Object} config The config to pass to the fieldset
47291      * @param {Field} field1 (optional)
47292      * @param {Field} field2 (optional)
47293      * @param {Field} etc (optional)
47294      * @return FieldSet The fieldset container object
47295      */
47296     fieldset : function(c){
47297         var fs = new Roo.form.FieldSet(c);
47298         this.start(fs);
47299         if(arguments.length > 1){ // duplicate code required because of Opera
47300             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47301             this.end();
47302         }
47303         return fs;
47304     },
47305
47306     /**
47307      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47308      * fields are added and the container is closed. If no fields are passed the container remains open
47309      * until end() is called.
47310      * @param {Object} config The config to pass to the Layout
47311      * @param {Field} field1 (optional)
47312      * @param {Field} field2 (optional)
47313      * @param {Field} etc (optional)
47314      * @return Layout The container object
47315      */
47316     container : function(c){
47317         var l = new Roo.form.Layout(c);
47318         this.start(l);
47319         if(arguments.length > 1){ // duplicate code required because of Opera
47320             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47321             this.end();
47322         }
47323         return l;
47324     },
47325
47326     /**
47327      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47328      * @param {Object} container A Roo.form.Layout or subclass of Layout
47329      * @return {Form} this
47330      */
47331     start : function(c){
47332         // cascade label info
47333         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47334         this.active.stack.push(c);
47335         c.ownerCt = this.active;
47336         this.active = c;
47337         return this;
47338     },
47339
47340     /**
47341      * Closes the current open container
47342      * @return {Form} this
47343      */
47344     end : function(){
47345         if(this.active == this.root){
47346             return this;
47347         }
47348         this.active = this.active.ownerCt;
47349         return this;
47350     },
47351
47352     /**
47353      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47354      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47355      * as the label of the field.
47356      * @param {Field} field1
47357      * @param {Field} field2 (optional)
47358      * @param {Field} etc. (optional)
47359      * @return {Form} this
47360      */
47361     add : function(){
47362         this.active.stack.push.apply(this.active.stack, arguments);
47363         this.allItems.push.apply(this.allItems,arguments);
47364         var r = [];
47365         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47366             if(a[i].isFormField){
47367                 r.push(a[i]);
47368             }
47369         }
47370         if(r.length > 0){
47371             Roo.form.Form.superclass.add.apply(this, r);
47372         }
47373         return this;
47374     },
47375     
47376
47377     
47378     
47379     
47380      /**
47381      * Find any element that has been added to a form, using it's ID or name
47382      * This can include framesets, columns etc. along with regular fields..
47383      * @param {String} id - id or name to find.
47384      
47385      * @return {Element} e - or false if nothing found.
47386      */
47387     findbyId : function(id)
47388     {
47389         var ret = false;
47390         if (!id) {
47391             return ret;
47392         }
47393         Roo.each(this.allItems, function(f){
47394             if (f.id == id || f.name == id ){
47395                 ret = f;
47396                 return false;
47397             }
47398         });
47399         return ret;
47400     },
47401
47402     
47403     
47404     /**
47405      * Render this form into the passed container. This should only be called once!
47406      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47407      * @return {Form} this
47408      */
47409     render : function(ct)
47410     {
47411         
47412         
47413         
47414         ct = Roo.get(ct);
47415         var o = this.autoCreate || {
47416             tag: 'form',
47417             method : this.method || 'POST',
47418             id : this.id || Roo.id()
47419         };
47420         this.initEl(ct.createChild(o));
47421
47422         this.root.render(this.el);
47423         
47424        
47425              
47426         this.items.each(function(f){
47427             f.render('x-form-el-'+f.id);
47428         });
47429
47430         if(this.buttons.length > 0){
47431             // tables are required to maintain order and for correct IE layout
47432             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47433                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47434                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47435             }}, null, true);
47436             var tr = tb.getElementsByTagName('tr')[0];
47437             for(var i = 0, len = this.buttons.length; i < len; i++) {
47438                 var b = this.buttons[i];
47439                 var td = document.createElement('td');
47440                 td.className = 'x-form-btn-td';
47441                 b.render(tr.appendChild(td));
47442             }
47443         }
47444         if(this.monitorValid){ // initialize after render
47445             this.startMonitoring();
47446         }
47447         this.fireEvent('rendered', this);
47448         return this;
47449     },
47450
47451     /**
47452      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47453      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47454      * object or a valid Roo.DomHelper element config
47455      * @param {Function} handler The function called when the button is clicked
47456      * @param {Object} scope (optional) The scope of the handler function
47457      * @return {Roo.Button}
47458      */
47459     addButton : function(config, handler, scope){
47460         var bc = {
47461             handler: handler,
47462             scope: scope,
47463             minWidth: this.minButtonWidth,
47464             hideParent:true
47465         };
47466         if(typeof config == "string"){
47467             bc.text = config;
47468         }else{
47469             Roo.apply(bc, config);
47470         }
47471         var btn = new Roo.Button(null, bc);
47472         this.buttons.push(btn);
47473         return btn;
47474     },
47475
47476      /**
47477      * Adds a series of form elements (using the xtype property as the factory method.
47478      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47479      * @param {Object} config 
47480      */
47481     
47482     addxtype : function()
47483     {
47484         var ar = Array.prototype.slice.call(arguments, 0);
47485         var ret = false;
47486         for(var i = 0; i < ar.length; i++) {
47487             if (!ar[i]) {
47488                 continue; // skip -- if this happends something invalid got sent, we 
47489                 // should ignore it, as basically that interface element will not show up
47490                 // and that should be pretty obvious!!
47491             }
47492             
47493             if (Roo.form[ar[i].xtype]) {
47494                 ar[i].form = this;
47495                 var fe = Roo.factory(ar[i], Roo.form);
47496                 if (!ret) {
47497                     ret = fe;
47498                 }
47499                 fe.form = this;
47500                 if (fe.store) {
47501                     fe.store.form = this;
47502                 }
47503                 if (fe.isLayout) {  
47504                          
47505                     this.start(fe);
47506                     this.allItems.push(fe);
47507                     if (fe.items && fe.addxtype) {
47508                         fe.addxtype.apply(fe, fe.items);
47509                         delete fe.items;
47510                     }
47511                      this.end();
47512                     continue;
47513                 }
47514                 
47515                 
47516                  
47517                 this.add(fe);
47518               //  console.log('adding ' + ar[i].xtype);
47519             }
47520             if (ar[i].xtype == 'Button') {  
47521                 //console.log('adding button');
47522                 //console.log(ar[i]);
47523                 this.addButton(ar[i]);
47524                 this.allItems.push(fe);
47525                 continue;
47526             }
47527             
47528             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47529                 alert('end is not supported on xtype any more, use items');
47530             //    this.end();
47531             //    //console.log('adding end');
47532             }
47533             
47534         }
47535         return ret;
47536     },
47537     
47538     /**
47539      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47540      * option "monitorValid"
47541      */
47542     startMonitoring : function(){
47543         if(!this.bound){
47544             this.bound = true;
47545             Roo.TaskMgr.start({
47546                 run : this.bindHandler,
47547                 interval : this.monitorPoll || 200,
47548                 scope: this
47549             });
47550         }
47551     },
47552
47553     /**
47554      * Stops monitoring of the valid state of this form
47555      */
47556     stopMonitoring : function(){
47557         this.bound = false;
47558     },
47559
47560     // private
47561     bindHandler : function(){
47562         if(!this.bound){
47563             return false; // stops binding
47564         }
47565         var valid = true;
47566         this.items.each(function(f){
47567             if(!f.isValid(true)){
47568                 valid = false;
47569                 return false;
47570             }
47571         });
47572         for(var i = 0, len = this.buttons.length; i < len; i++){
47573             var btn = this.buttons[i];
47574             if(btn.formBind === true && btn.disabled === valid){
47575                 btn.setDisabled(!valid);
47576             }
47577         }
47578         this.fireEvent('clientvalidation', this, valid);
47579     }
47580     
47581     
47582     
47583     
47584     
47585     
47586     
47587     
47588 });
47589
47590
47591 // back compat
47592 Roo.Form = Roo.form.Form;
47593 /*
47594  * Based on:
47595  * Ext JS Library 1.1.1
47596  * Copyright(c) 2006-2007, Ext JS, LLC.
47597  *
47598  * Originally Released Under LGPL - original licence link has changed is not relivant.
47599  *
47600  * Fork - LGPL
47601  * <script type="text/javascript">
47602  */
47603
47604 // as we use this in bootstrap.
47605 Roo.namespace('Roo.form');
47606  /**
47607  * @class Roo.form.Action
47608  * Internal Class used to handle form actions
47609  * @constructor
47610  * @param {Roo.form.BasicForm} el The form element or its id
47611  * @param {Object} config Configuration options
47612  */
47613
47614  
47615  
47616 // define the action interface
47617 Roo.form.Action = function(form, options){
47618     this.form = form;
47619     this.options = options || {};
47620 };
47621 /**
47622  * Client Validation Failed
47623  * @const 
47624  */
47625 Roo.form.Action.CLIENT_INVALID = 'client';
47626 /**
47627  * Server Validation Failed
47628  * @const 
47629  */
47630 Roo.form.Action.SERVER_INVALID = 'server';
47631  /**
47632  * Connect to Server Failed
47633  * @const 
47634  */
47635 Roo.form.Action.CONNECT_FAILURE = 'connect';
47636 /**
47637  * Reading Data from Server Failed
47638  * @const 
47639  */
47640 Roo.form.Action.LOAD_FAILURE = 'load';
47641
47642 Roo.form.Action.prototype = {
47643     type : 'default',
47644     failureType : undefined,
47645     response : undefined,
47646     result : undefined,
47647
47648     // interface method
47649     run : function(options){
47650
47651     },
47652
47653     // interface method
47654     success : function(response){
47655
47656     },
47657
47658     // interface method
47659     handleResponse : function(response){
47660
47661     },
47662
47663     // default connection failure
47664     failure : function(response){
47665         
47666         this.response = response;
47667         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47668         this.form.afterAction(this, false);
47669     },
47670
47671     processResponse : function(response){
47672         this.response = response;
47673         if(!response.responseText){
47674             return true;
47675         }
47676         this.result = this.handleResponse(response);
47677         return this.result;
47678     },
47679
47680     // utility functions used internally
47681     getUrl : function(appendParams){
47682         var url = this.options.url || this.form.url || this.form.el.dom.action;
47683         if(appendParams){
47684             var p = this.getParams();
47685             if(p){
47686                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47687             }
47688         }
47689         return url;
47690     },
47691
47692     getMethod : function(){
47693         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47694     },
47695
47696     getParams : function(){
47697         var bp = this.form.baseParams;
47698         var p = this.options.params;
47699         if(p){
47700             if(typeof p == "object"){
47701                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47702             }else if(typeof p == 'string' && bp){
47703                 p += '&' + Roo.urlEncode(bp);
47704             }
47705         }else if(bp){
47706             p = Roo.urlEncode(bp);
47707         }
47708         return p;
47709     },
47710
47711     createCallback : function(){
47712         return {
47713             success: this.success,
47714             failure: this.failure,
47715             scope: this,
47716             timeout: (this.form.timeout*1000),
47717             upload: this.form.fileUpload ? this.success : undefined
47718         };
47719     }
47720 };
47721
47722 Roo.form.Action.Submit = function(form, options){
47723     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47724 };
47725
47726 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47727     type : 'submit',
47728
47729     haveProgress : false,
47730     uploadComplete : false,
47731     
47732     // uploadProgress indicator.
47733     uploadProgress : function()
47734     {
47735         if (!this.form.progressUrl) {
47736             return;
47737         }
47738         
47739         if (!this.haveProgress) {
47740             Roo.MessageBox.progress("Uploading", "Uploading");
47741         }
47742         if (this.uploadComplete) {
47743            Roo.MessageBox.hide();
47744            return;
47745         }
47746         
47747         this.haveProgress = true;
47748    
47749         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47750         
47751         var c = new Roo.data.Connection();
47752         c.request({
47753             url : this.form.progressUrl,
47754             params: {
47755                 id : uid
47756             },
47757             method: 'GET',
47758             success : function(req){
47759                //console.log(data);
47760                 var rdata = false;
47761                 var edata;
47762                 try  {
47763                    rdata = Roo.decode(req.responseText)
47764                 } catch (e) {
47765                     Roo.log("Invalid data from server..");
47766                     Roo.log(edata);
47767                     return;
47768                 }
47769                 if (!rdata || !rdata.success) {
47770                     Roo.log(rdata);
47771                     Roo.MessageBox.alert(Roo.encode(rdata));
47772                     return;
47773                 }
47774                 var data = rdata.data;
47775                 
47776                 if (this.uploadComplete) {
47777                    Roo.MessageBox.hide();
47778                    return;
47779                 }
47780                    
47781                 if (data){
47782                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47783                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47784                     );
47785                 }
47786                 this.uploadProgress.defer(2000,this);
47787             },
47788        
47789             failure: function(data) {
47790                 Roo.log('progress url failed ');
47791                 Roo.log(data);
47792             },
47793             scope : this
47794         });
47795            
47796     },
47797     
47798     
47799     run : function()
47800     {
47801         // run get Values on the form, so it syncs any secondary forms.
47802         this.form.getValues();
47803         
47804         var o = this.options;
47805         var method = this.getMethod();
47806         var isPost = method == 'POST';
47807         if(o.clientValidation === false || this.form.isValid()){
47808             
47809             if (this.form.progressUrl) {
47810                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47811                     (new Date() * 1) + '' + Math.random());
47812                     
47813             } 
47814             
47815             
47816             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47817                 form:this.form.el.dom,
47818                 url:this.getUrl(!isPost),
47819                 method: method,
47820                 params:isPost ? this.getParams() : null,
47821                 isUpload: this.form.fileUpload
47822             }));
47823             
47824             this.uploadProgress();
47825
47826         }else if (o.clientValidation !== false){ // client validation failed
47827             this.failureType = Roo.form.Action.CLIENT_INVALID;
47828             this.form.afterAction(this, false);
47829         }
47830     },
47831
47832     success : function(response)
47833     {
47834         this.uploadComplete= true;
47835         if (this.haveProgress) {
47836             Roo.MessageBox.hide();
47837         }
47838         
47839         
47840         var result = this.processResponse(response);
47841         if(result === true || result.success){
47842             this.form.afterAction(this, true);
47843             return;
47844         }
47845         if(result.errors){
47846             this.form.markInvalid(result.errors);
47847             this.failureType = Roo.form.Action.SERVER_INVALID;
47848         }
47849         this.form.afterAction(this, false);
47850     },
47851     failure : function(response)
47852     {
47853         this.uploadComplete= true;
47854         if (this.haveProgress) {
47855             Roo.MessageBox.hide();
47856         }
47857         
47858         this.response = response;
47859         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47860         this.form.afterAction(this, false);
47861     },
47862     
47863     handleResponse : function(response){
47864         if(this.form.errorReader){
47865             var rs = this.form.errorReader.read(response);
47866             var errors = [];
47867             if(rs.records){
47868                 for(var i = 0, len = rs.records.length; i < len; i++) {
47869                     var r = rs.records[i];
47870                     errors[i] = r.data;
47871                 }
47872             }
47873             if(errors.length < 1){
47874                 errors = null;
47875             }
47876             return {
47877                 success : rs.success,
47878                 errors : errors
47879             };
47880         }
47881         var ret = false;
47882         try {
47883             ret = Roo.decode(response.responseText);
47884         } catch (e) {
47885             ret = {
47886                 success: false,
47887                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47888                 errors : []
47889             };
47890         }
47891         return ret;
47892         
47893     }
47894 });
47895
47896
47897 Roo.form.Action.Load = function(form, options){
47898     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47899     this.reader = this.form.reader;
47900 };
47901
47902 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47903     type : 'load',
47904
47905     run : function(){
47906         
47907         Roo.Ajax.request(Roo.apply(
47908                 this.createCallback(), {
47909                     method:this.getMethod(),
47910                     url:this.getUrl(false),
47911                     params:this.getParams()
47912         }));
47913     },
47914
47915     success : function(response){
47916         
47917         var result = this.processResponse(response);
47918         if(result === true || !result.success || !result.data){
47919             this.failureType = Roo.form.Action.LOAD_FAILURE;
47920             this.form.afterAction(this, false);
47921             return;
47922         }
47923         this.form.clearInvalid();
47924         this.form.setValues(result.data);
47925         this.form.afterAction(this, true);
47926     },
47927
47928     handleResponse : function(response){
47929         if(this.form.reader){
47930             var rs = this.form.reader.read(response);
47931             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47932             return {
47933                 success : rs.success,
47934                 data : data
47935             };
47936         }
47937         return Roo.decode(response.responseText);
47938     }
47939 });
47940
47941 Roo.form.Action.ACTION_TYPES = {
47942     'load' : Roo.form.Action.Load,
47943     'submit' : Roo.form.Action.Submit
47944 };/*
47945  * Based on:
47946  * Ext JS Library 1.1.1
47947  * Copyright(c) 2006-2007, Ext JS, LLC.
47948  *
47949  * Originally Released Under LGPL - original licence link has changed is not relivant.
47950  *
47951  * Fork - LGPL
47952  * <script type="text/javascript">
47953  */
47954  
47955 /**
47956  * @class Roo.form.Layout
47957  * @extends Roo.Component
47958  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
47959  * @constructor
47960  * @param {Object} config Configuration options
47961  */
47962 Roo.form.Layout = function(config){
47963     var xitems = [];
47964     if (config.items) {
47965         xitems = config.items;
47966         delete config.items;
47967     }
47968     Roo.form.Layout.superclass.constructor.call(this, config);
47969     this.stack = [];
47970     Roo.each(xitems, this.addxtype, this);
47971      
47972 };
47973
47974 Roo.extend(Roo.form.Layout, Roo.Component, {
47975     /**
47976      * @cfg {String/Object} autoCreate
47977      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
47978      */
47979     /**
47980      * @cfg {String/Object/Function} style
47981      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
47982      * a function which returns such a specification.
47983      */
47984     /**
47985      * @cfg {String} labelAlign
47986      * Valid values are "left," "top" and "right" (defaults to "left")
47987      */
47988     /**
47989      * @cfg {Number} labelWidth
47990      * Fixed width in pixels of all field labels (defaults to undefined)
47991      */
47992     /**
47993      * @cfg {Boolean} clear
47994      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
47995      */
47996     clear : true,
47997     /**
47998      * @cfg {String} labelSeparator
47999      * The separator to use after field labels (defaults to ':')
48000      */
48001     labelSeparator : ':',
48002     /**
48003      * @cfg {Boolean} hideLabels
48004      * True to suppress the display of field labels in this layout (defaults to false)
48005      */
48006     hideLabels : false,
48007
48008     // private
48009     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48010     
48011     isLayout : true,
48012     
48013     // private
48014     onRender : function(ct, position){
48015         if(this.el){ // from markup
48016             this.el = Roo.get(this.el);
48017         }else {  // generate
48018             var cfg = this.getAutoCreate();
48019             this.el = ct.createChild(cfg, position);
48020         }
48021         if(this.style){
48022             this.el.applyStyles(this.style);
48023         }
48024         if(this.labelAlign){
48025             this.el.addClass('x-form-label-'+this.labelAlign);
48026         }
48027         if(this.hideLabels){
48028             this.labelStyle = "display:none";
48029             this.elementStyle = "padding-left:0;";
48030         }else{
48031             if(typeof this.labelWidth == 'number'){
48032                 this.labelStyle = "width:"+this.labelWidth+"px;";
48033                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48034             }
48035             if(this.labelAlign == 'top'){
48036                 this.labelStyle = "width:auto;";
48037                 this.elementStyle = "padding-left:0;";
48038             }
48039         }
48040         var stack = this.stack;
48041         var slen = stack.length;
48042         if(slen > 0){
48043             if(!this.fieldTpl){
48044                 var t = new Roo.Template(
48045                     '<div class="x-form-item {5}">',
48046                         '<label for="{0}" style="{2}">{1}{4}</label>',
48047                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48048                         '</div>',
48049                     '</div><div class="x-form-clear-left"></div>'
48050                 );
48051                 t.disableFormats = true;
48052                 t.compile();
48053                 Roo.form.Layout.prototype.fieldTpl = t;
48054             }
48055             for(var i = 0; i < slen; i++) {
48056                 if(stack[i].isFormField){
48057                     this.renderField(stack[i]);
48058                 }else{
48059                     this.renderComponent(stack[i]);
48060                 }
48061             }
48062         }
48063         if(this.clear){
48064             this.el.createChild({cls:'x-form-clear'});
48065         }
48066     },
48067
48068     // private
48069     renderField : function(f){
48070         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48071                f.id, //0
48072                f.fieldLabel, //1
48073                f.labelStyle||this.labelStyle||'', //2
48074                this.elementStyle||'', //3
48075                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48076                f.itemCls||this.itemCls||''  //5
48077        ], true).getPrevSibling());
48078     },
48079
48080     // private
48081     renderComponent : function(c){
48082         c.render(c.isLayout ? this.el : this.el.createChild());    
48083     },
48084     /**
48085      * Adds a object form elements (using the xtype property as the factory method.)
48086      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48087      * @param {Object} config 
48088      */
48089     addxtype : function(o)
48090     {
48091         // create the lement.
48092         o.form = this.form;
48093         var fe = Roo.factory(o, Roo.form);
48094         this.form.allItems.push(fe);
48095         this.stack.push(fe);
48096         
48097         if (fe.isFormField) {
48098             this.form.items.add(fe);
48099         }
48100          
48101         return fe;
48102     }
48103 });
48104
48105 /**
48106  * @class Roo.form.Column
48107  * @extends Roo.form.Layout
48108  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48109  * @constructor
48110  * @param {Object} config Configuration options
48111  */
48112 Roo.form.Column = function(config){
48113     Roo.form.Column.superclass.constructor.call(this, config);
48114 };
48115
48116 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48117     /**
48118      * @cfg {Number/String} width
48119      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48120      */
48121     /**
48122      * @cfg {String/Object} autoCreate
48123      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48124      */
48125
48126     // private
48127     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48128
48129     // private
48130     onRender : function(ct, position){
48131         Roo.form.Column.superclass.onRender.call(this, ct, position);
48132         if(this.width){
48133             this.el.setWidth(this.width);
48134         }
48135     }
48136 });
48137
48138
48139 /**
48140  * @class Roo.form.Row
48141  * @extends Roo.form.Layout
48142  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48143  * @constructor
48144  * @param {Object} config Configuration options
48145  */
48146
48147  
48148 Roo.form.Row = function(config){
48149     Roo.form.Row.superclass.constructor.call(this, config);
48150 };
48151  
48152 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48153       /**
48154      * @cfg {Number/String} width
48155      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48156      */
48157     /**
48158      * @cfg {Number/String} height
48159      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48160      */
48161     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48162     
48163     padWidth : 20,
48164     // private
48165     onRender : function(ct, position){
48166         //console.log('row render');
48167         if(!this.rowTpl){
48168             var t = new Roo.Template(
48169                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48170                     '<label for="{0}" style="{2}">{1}{4}</label>',
48171                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48172                     '</div>',
48173                 '</div>'
48174             );
48175             t.disableFormats = true;
48176             t.compile();
48177             Roo.form.Layout.prototype.rowTpl = t;
48178         }
48179         this.fieldTpl = this.rowTpl;
48180         
48181         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48182         var labelWidth = 100;
48183         
48184         if ((this.labelAlign != 'top')) {
48185             if (typeof this.labelWidth == 'number') {
48186                 labelWidth = this.labelWidth
48187             }
48188             this.padWidth =  20 + labelWidth;
48189             
48190         }
48191         
48192         Roo.form.Column.superclass.onRender.call(this, ct, position);
48193         if(this.width){
48194             this.el.setWidth(this.width);
48195         }
48196         if(this.height){
48197             this.el.setHeight(this.height);
48198         }
48199     },
48200     
48201     // private
48202     renderField : function(f){
48203         f.fieldEl = this.fieldTpl.append(this.el, [
48204                f.id, f.fieldLabel,
48205                f.labelStyle||this.labelStyle||'',
48206                this.elementStyle||'',
48207                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48208                f.itemCls||this.itemCls||'',
48209                f.width ? f.width + this.padWidth : 160 + this.padWidth
48210        ],true);
48211     }
48212 });
48213  
48214
48215 /**
48216  * @class Roo.form.FieldSet
48217  * @extends Roo.form.Layout
48218  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48219  * @constructor
48220  * @param {Object} config Configuration options
48221  */
48222 Roo.form.FieldSet = function(config){
48223     Roo.form.FieldSet.superclass.constructor.call(this, config);
48224 };
48225
48226 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48227     /**
48228      * @cfg {String} legend
48229      * The text to display as the legend for the FieldSet (defaults to '')
48230      */
48231     /**
48232      * @cfg {String/Object} autoCreate
48233      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48234      */
48235
48236     // private
48237     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48238
48239     // private
48240     onRender : function(ct, position){
48241         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48242         if(this.legend){
48243             this.setLegend(this.legend);
48244         }
48245     },
48246
48247     // private
48248     setLegend : function(text){
48249         if(this.rendered){
48250             this.el.child('legend').update(text);
48251         }
48252     }
48253 });/*
48254  * Based on:
48255  * Ext JS Library 1.1.1
48256  * Copyright(c) 2006-2007, Ext JS, LLC.
48257  *
48258  * Originally Released Under LGPL - original licence link has changed is not relivant.
48259  *
48260  * Fork - LGPL
48261  * <script type="text/javascript">
48262  */
48263 /**
48264  * @class Roo.form.VTypes
48265  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48266  * @singleton
48267  */
48268 Roo.form.VTypes = function(){
48269     // closure these in so they are only created once.
48270     var alpha = /^[a-zA-Z_]+$/;
48271     var alphanum = /^[a-zA-Z0-9_]+$/;
48272     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48273     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48274
48275     // All these messages and functions are configurable
48276     return {
48277         /**
48278          * The function used to validate email addresses
48279          * @param {String} value The email address
48280          */
48281         'email' : function(v){
48282             return email.test(v);
48283         },
48284         /**
48285          * The error text to display when the email validation function returns false
48286          * @type String
48287          */
48288         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48289         /**
48290          * The keystroke filter mask to be applied on email input
48291          * @type RegExp
48292          */
48293         'emailMask' : /[a-z0-9_\.\-@]/i,
48294
48295         /**
48296          * The function used to validate URLs
48297          * @param {String} value The URL
48298          */
48299         'url' : function(v){
48300             return url.test(v);
48301         },
48302         /**
48303          * The error text to display when the url validation function returns false
48304          * @type String
48305          */
48306         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48307         
48308         /**
48309          * The function used to validate alpha values
48310          * @param {String} value The value
48311          */
48312         'alpha' : function(v){
48313             return alpha.test(v);
48314         },
48315         /**
48316          * The error text to display when the alpha validation function returns false
48317          * @type String
48318          */
48319         'alphaText' : 'This field should only contain letters and _',
48320         /**
48321          * The keystroke filter mask to be applied on alpha input
48322          * @type RegExp
48323          */
48324         'alphaMask' : /[a-z_]/i,
48325
48326         /**
48327          * The function used to validate alphanumeric values
48328          * @param {String} value The value
48329          */
48330         'alphanum' : function(v){
48331             return alphanum.test(v);
48332         },
48333         /**
48334          * The error text to display when the alphanumeric validation function returns false
48335          * @type String
48336          */
48337         'alphanumText' : 'This field should only contain letters, numbers and _',
48338         /**
48339          * The keystroke filter mask to be applied on alphanumeric input
48340          * @type RegExp
48341          */
48342         'alphanumMask' : /[a-z0-9_]/i
48343     };
48344 }();//<script type="text/javascript">
48345
48346 /**
48347  * @class Roo.form.FCKeditor
48348  * @extends Roo.form.TextArea
48349  * Wrapper around the FCKEditor http://www.fckeditor.net
48350  * @constructor
48351  * Creates a new FCKeditor
48352  * @param {Object} config Configuration options
48353  */
48354 Roo.form.FCKeditor = function(config){
48355     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48356     this.addEvents({
48357          /**
48358          * @event editorinit
48359          * Fired when the editor is initialized - you can add extra handlers here..
48360          * @param {FCKeditor} this
48361          * @param {Object} the FCK object.
48362          */
48363         editorinit : true
48364     });
48365     
48366     
48367 };
48368 Roo.form.FCKeditor.editors = { };
48369 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48370 {
48371     //defaultAutoCreate : {
48372     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48373     //},
48374     // private
48375     /**
48376      * @cfg {Object} fck options - see fck manual for details.
48377      */
48378     fckconfig : false,
48379     
48380     /**
48381      * @cfg {Object} fck toolbar set (Basic or Default)
48382      */
48383     toolbarSet : 'Basic',
48384     /**
48385      * @cfg {Object} fck BasePath
48386      */ 
48387     basePath : '/fckeditor/',
48388     
48389     
48390     frame : false,
48391     
48392     value : '',
48393     
48394    
48395     onRender : function(ct, position)
48396     {
48397         if(!this.el){
48398             this.defaultAutoCreate = {
48399                 tag: "textarea",
48400                 style:"width:300px;height:60px;",
48401                 autocomplete: "new-password"
48402             };
48403         }
48404         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48405         /*
48406         if(this.grow){
48407             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48408             if(this.preventScrollbars){
48409                 this.el.setStyle("overflow", "hidden");
48410             }
48411             this.el.setHeight(this.growMin);
48412         }
48413         */
48414         //console.log('onrender' + this.getId() );
48415         Roo.form.FCKeditor.editors[this.getId()] = this;
48416          
48417
48418         this.replaceTextarea() ;
48419         
48420     },
48421     
48422     getEditor : function() {
48423         return this.fckEditor;
48424     },
48425     /**
48426      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48427      * @param {Mixed} value The value to set
48428      */
48429     
48430     
48431     setValue : function(value)
48432     {
48433         //console.log('setValue: ' + value);
48434         
48435         if(typeof(value) == 'undefined') { // not sure why this is happending...
48436             return;
48437         }
48438         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48439         
48440         //if(!this.el || !this.getEditor()) {
48441         //    this.value = value;
48442             //this.setValue.defer(100,this,[value]);    
48443         //    return;
48444         //} 
48445         
48446         if(!this.getEditor()) {
48447             return;
48448         }
48449         
48450         this.getEditor().SetData(value);
48451         
48452         //
48453
48454     },
48455
48456     /**
48457      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48458      * @return {Mixed} value The field value
48459      */
48460     getValue : function()
48461     {
48462         
48463         if (this.frame && this.frame.dom.style.display == 'none') {
48464             return Roo.form.FCKeditor.superclass.getValue.call(this);
48465         }
48466         
48467         if(!this.el || !this.getEditor()) {
48468            
48469            // this.getValue.defer(100,this); 
48470             return this.value;
48471         }
48472        
48473         
48474         var value=this.getEditor().GetData();
48475         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48476         return Roo.form.FCKeditor.superclass.getValue.call(this);
48477         
48478
48479     },
48480
48481     /**
48482      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48483      * @return {Mixed} value The field value
48484      */
48485     getRawValue : function()
48486     {
48487         if (this.frame && this.frame.dom.style.display == 'none') {
48488             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48489         }
48490         
48491         if(!this.el || !this.getEditor()) {
48492             //this.getRawValue.defer(100,this); 
48493             return this.value;
48494             return;
48495         }
48496         
48497         
48498         
48499         var value=this.getEditor().GetData();
48500         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48501         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48502          
48503     },
48504     
48505     setSize : function(w,h) {
48506         
48507         
48508         
48509         //if (this.frame && this.frame.dom.style.display == 'none') {
48510         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48511         //    return;
48512         //}
48513         //if(!this.el || !this.getEditor()) {
48514         //    this.setSize.defer(100,this, [w,h]); 
48515         //    return;
48516         //}
48517         
48518         
48519         
48520         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48521         
48522         this.frame.dom.setAttribute('width', w);
48523         this.frame.dom.setAttribute('height', h);
48524         this.frame.setSize(w,h);
48525         
48526     },
48527     
48528     toggleSourceEdit : function(value) {
48529         
48530       
48531          
48532         this.el.dom.style.display = value ? '' : 'none';
48533         this.frame.dom.style.display = value ?  'none' : '';
48534         
48535     },
48536     
48537     
48538     focus: function(tag)
48539     {
48540         if (this.frame.dom.style.display == 'none') {
48541             return Roo.form.FCKeditor.superclass.focus.call(this);
48542         }
48543         if(!this.el || !this.getEditor()) {
48544             this.focus.defer(100,this, [tag]); 
48545             return;
48546         }
48547         
48548         
48549         
48550         
48551         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48552         this.getEditor().Focus();
48553         if (tgs.length) {
48554             if (!this.getEditor().Selection.GetSelection()) {
48555                 this.focus.defer(100,this, [tag]); 
48556                 return;
48557             }
48558             
48559             
48560             var r = this.getEditor().EditorDocument.createRange();
48561             r.setStart(tgs[0],0);
48562             r.setEnd(tgs[0],0);
48563             this.getEditor().Selection.GetSelection().removeAllRanges();
48564             this.getEditor().Selection.GetSelection().addRange(r);
48565             this.getEditor().Focus();
48566         }
48567         
48568     },
48569     
48570     
48571     
48572     replaceTextarea : function()
48573     {
48574         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48575             return ;
48576         }
48577         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48578         //{
48579             // We must check the elements firstly using the Id and then the name.
48580         var oTextarea = document.getElementById( this.getId() );
48581         
48582         var colElementsByName = document.getElementsByName( this.getId() ) ;
48583          
48584         oTextarea.style.display = 'none' ;
48585
48586         if ( oTextarea.tabIndex ) {            
48587             this.TabIndex = oTextarea.tabIndex ;
48588         }
48589         
48590         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48591         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48592         this.frame = Roo.get(this.getId() + '___Frame')
48593     },
48594     
48595     _getConfigHtml : function()
48596     {
48597         var sConfig = '' ;
48598
48599         for ( var o in this.fckconfig ) {
48600             sConfig += sConfig.length > 0  ? '&amp;' : '';
48601             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48602         }
48603
48604         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48605     },
48606     
48607     
48608     _getIFrameHtml : function()
48609     {
48610         var sFile = 'fckeditor.html' ;
48611         /* no idea what this is about..
48612         try
48613         {
48614             if ( (/fcksource=true/i).test( window.top.location.search ) )
48615                 sFile = 'fckeditor.original.html' ;
48616         }
48617         catch (e) { 
48618         */
48619
48620         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48621         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48622         
48623         
48624         var html = '<iframe id="' + this.getId() +
48625             '___Frame" src="' + sLink +
48626             '" width="' + this.width +
48627             '" height="' + this.height + '"' +
48628             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48629             ' frameborder="0" scrolling="no"></iframe>' ;
48630
48631         return html ;
48632     },
48633     
48634     _insertHtmlBefore : function( html, element )
48635     {
48636         if ( element.insertAdjacentHTML )       {
48637             // IE
48638             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48639         } else { // Gecko
48640             var oRange = document.createRange() ;
48641             oRange.setStartBefore( element ) ;
48642             var oFragment = oRange.createContextualFragment( html );
48643             element.parentNode.insertBefore( oFragment, element ) ;
48644         }
48645     }
48646     
48647     
48648   
48649     
48650     
48651     
48652     
48653
48654 });
48655
48656 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48657
48658 function FCKeditor_OnComplete(editorInstance){
48659     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48660     f.fckEditor = editorInstance;
48661     //console.log("loaded");
48662     f.fireEvent('editorinit', f, editorInstance);
48663
48664   
48665
48666  
48667
48668
48669
48670
48671
48672
48673
48674
48675
48676
48677
48678
48679
48680
48681
48682 //<script type="text/javascript">
48683 /**
48684  * @class Roo.form.GridField
48685  * @extends Roo.form.Field
48686  * Embed a grid (or editable grid into a form)
48687  * STATUS ALPHA
48688  * 
48689  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48690  * it needs 
48691  * xgrid.store = Roo.data.Store
48692  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48693  * xgrid.store.reader = Roo.data.JsonReader 
48694  * 
48695  * 
48696  * @constructor
48697  * Creates a new GridField
48698  * @param {Object} config Configuration options
48699  */
48700 Roo.form.GridField = function(config){
48701     Roo.form.GridField.superclass.constructor.call(this, config);
48702      
48703 };
48704
48705 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48706     /**
48707      * @cfg {Number} width  - used to restrict width of grid..
48708      */
48709     width : 100,
48710     /**
48711      * @cfg {Number} height - used to restrict height of grid..
48712      */
48713     height : 50,
48714      /**
48715      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48716          * 
48717          *}
48718      */
48719     xgrid : false, 
48720     /**
48721      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48722      * {tag: "input", type: "checkbox", autocomplete: "off"})
48723      */
48724    // defaultAutoCreate : { tag: 'div' },
48725     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48726     /**
48727      * @cfg {String} addTitle Text to include for adding a title.
48728      */
48729     addTitle : false,
48730     //
48731     onResize : function(){
48732         Roo.form.Field.superclass.onResize.apply(this, arguments);
48733     },
48734
48735     initEvents : function(){
48736         // Roo.form.Checkbox.superclass.initEvents.call(this);
48737         // has no events...
48738        
48739     },
48740
48741
48742     getResizeEl : function(){
48743         return this.wrap;
48744     },
48745
48746     getPositionEl : function(){
48747         return this.wrap;
48748     },
48749
48750     // private
48751     onRender : function(ct, position){
48752         
48753         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48754         var style = this.style;
48755         delete this.style;
48756         
48757         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48758         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48759         this.viewEl = this.wrap.createChild({ tag: 'div' });
48760         if (style) {
48761             this.viewEl.applyStyles(style);
48762         }
48763         if (this.width) {
48764             this.viewEl.setWidth(this.width);
48765         }
48766         if (this.height) {
48767             this.viewEl.setHeight(this.height);
48768         }
48769         //if(this.inputValue !== undefined){
48770         //this.setValue(this.value);
48771         
48772         
48773         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48774         
48775         
48776         this.grid.render();
48777         this.grid.getDataSource().on('remove', this.refreshValue, this);
48778         this.grid.getDataSource().on('update', this.refreshValue, this);
48779         this.grid.on('afteredit', this.refreshValue, this);
48780  
48781     },
48782      
48783     
48784     /**
48785      * Sets the value of the item. 
48786      * @param {String} either an object  or a string..
48787      */
48788     setValue : function(v){
48789         //this.value = v;
48790         v = v || []; // empty set..
48791         // this does not seem smart - it really only affects memoryproxy grids..
48792         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48793             var ds = this.grid.getDataSource();
48794             // assumes a json reader..
48795             var data = {}
48796             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48797             ds.loadData( data);
48798         }
48799         // clear selection so it does not get stale.
48800         if (this.grid.sm) { 
48801             this.grid.sm.clearSelections();
48802         }
48803         
48804         Roo.form.GridField.superclass.setValue.call(this, v);
48805         this.refreshValue();
48806         // should load data in the grid really....
48807     },
48808     
48809     // private
48810     refreshValue: function() {
48811          var val = [];
48812         this.grid.getDataSource().each(function(r) {
48813             val.push(r.data);
48814         });
48815         this.el.dom.value = Roo.encode(val);
48816     }
48817     
48818      
48819     
48820     
48821 });/*
48822  * Based on:
48823  * Ext JS Library 1.1.1
48824  * Copyright(c) 2006-2007, Ext JS, LLC.
48825  *
48826  * Originally Released Under LGPL - original licence link has changed is not relivant.
48827  *
48828  * Fork - LGPL
48829  * <script type="text/javascript">
48830  */
48831 /**
48832  * @class Roo.form.DisplayField
48833  * @extends Roo.form.Field
48834  * A generic Field to display non-editable data.
48835  * @cfg {Boolean} closable (true|false) default false
48836  * @constructor
48837  * Creates a new Display Field item.
48838  * @param {Object} config Configuration options
48839  */
48840 Roo.form.DisplayField = function(config){
48841     Roo.form.DisplayField.superclass.constructor.call(this, config);
48842     
48843     this.addEvents({
48844         /**
48845          * @event close
48846          * Fires after the click the close btn
48847              * @param {Roo.form.DisplayField} this
48848              */
48849         close : true
48850     });
48851 };
48852
48853 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48854     inputType:      'hidden',
48855     allowBlank:     true,
48856     readOnly:         true,
48857     
48858  
48859     /**
48860      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48861      */
48862     focusClass : undefined,
48863     /**
48864      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48865      */
48866     fieldClass: 'x-form-field',
48867     
48868      /**
48869      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48870      */
48871     valueRenderer: undefined,
48872     
48873     width: 100,
48874     /**
48875      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48876      * {tag: "input", type: "checkbox", autocomplete: "off"})
48877      */
48878      
48879  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48880  
48881     closable : false,
48882     
48883     onResize : function(){
48884         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48885         
48886     },
48887
48888     initEvents : function(){
48889         // Roo.form.Checkbox.superclass.initEvents.call(this);
48890         // has no events...
48891         
48892         if(this.closable){
48893             this.closeEl.on('click', this.onClose, this);
48894         }
48895        
48896     },
48897
48898
48899     getResizeEl : function(){
48900         return this.wrap;
48901     },
48902
48903     getPositionEl : function(){
48904         return this.wrap;
48905     },
48906
48907     // private
48908     onRender : function(ct, position){
48909         
48910         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48911         //if(this.inputValue !== undefined){
48912         this.wrap = this.el.wrap();
48913         
48914         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48915         
48916         if(this.closable){
48917             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48918         }
48919         
48920         if (this.bodyStyle) {
48921             this.viewEl.applyStyles(this.bodyStyle);
48922         }
48923         //this.viewEl.setStyle('padding', '2px');
48924         
48925         this.setValue(this.value);
48926         
48927     },
48928 /*
48929     // private
48930     initValue : Roo.emptyFn,
48931
48932   */
48933
48934         // private
48935     onClick : function(){
48936         
48937     },
48938
48939     /**
48940      * Sets the checked state of the checkbox.
48941      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48942      */
48943     setValue : function(v){
48944         this.value = v;
48945         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48946         // this might be called before we have a dom element..
48947         if (!this.viewEl) {
48948             return;
48949         }
48950         this.viewEl.dom.innerHTML = html;
48951         Roo.form.DisplayField.superclass.setValue.call(this, v);
48952
48953     },
48954     
48955     onClose : function(e)
48956     {
48957         e.preventDefault();
48958         
48959         this.fireEvent('close', this);
48960     }
48961 });/*
48962  * 
48963  * Licence- LGPL
48964  * 
48965  */
48966
48967 /**
48968  * @class Roo.form.DayPicker
48969  * @extends Roo.form.Field
48970  * A Day picker show [M] [T] [W] ....
48971  * @constructor
48972  * Creates a new Day Picker
48973  * @param {Object} config Configuration options
48974  */
48975 Roo.form.DayPicker= function(config){
48976     Roo.form.DayPicker.superclass.constructor.call(this, config);
48977      
48978 };
48979
48980 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
48981     /**
48982      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48983      */
48984     focusClass : undefined,
48985     /**
48986      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48987      */
48988     fieldClass: "x-form-field",
48989    
48990     /**
48991      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48992      * {tag: "input", type: "checkbox", autocomplete: "off"})
48993      */
48994     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
48995     
48996    
48997     actionMode : 'viewEl', 
48998     //
48999     // private
49000  
49001     inputType : 'hidden',
49002     
49003      
49004     inputElement: false, // real input element?
49005     basedOn: false, // ????
49006     
49007     isFormField: true, // not sure where this is needed!!!!
49008
49009     onResize : function(){
49010         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49011         if(!this.boxLabel){
49012             this.el.alignTo(this.wrap, 'c-c');
49013         }
49014     },
49015
49016     initEvents : function(){
49017         Roo.form.Checkbox.superclass.initEvents.call(this);
49018         this.el.on("click", this.onClick,  this);
49019         this.el.on("change", this.onClick,  this);
49020     },
49021
49022
49023     getResizeEl : function(){
49024         return this.wrap;
49025     },
49026
49027     getPositionEl : function(){
49028         return this.wrap;
49029     },
49030
49031     
49032     // private
49033     onRender : function(ct, position){
49034         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49035        
49036         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49037         
49038         var r1 = '<table><tr>';
49039         var r2 = '<tr class="x-form-daypick-icons">';
49040         for (var i=0; i < 7; i++) {
49041             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49042             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49043         }
49044         
49045         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49046         viewEl.select('img').on('click', this.onClick, this);
49047         this.viewEl = viewEl;   
49048         
49049         
49050         // this will not work on Chrome!!!
49051         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49052         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49053         
49054         
49055           
49056
49057     },
49058
49059     // private
49060     initValue : Roo.emptyFn,
49061
49062     /**
49063      * Returns the checked state of the checkbox.
49064      * @return {Boolean} True if checked, else false
49065      */
49066     getValue : function(){
49067         return this.el.dom.value;
49068         
49069     },
49070
49071         // private
49072     onClick : function(e){ 
49073         //this.setChecked(!this.checked);
49074         Roo.get(e.target).toggleClass('x-menu-item-checked');
49075         this.refreshValue();
49076         //if(this.el.dom.checked != this.checked){
49077         //    this.setValue(this.el.dom.checked);
49078        // }
49079     },
49080     
49081     // private
49082     refreshValue : function()
49083     {
49084         var val = '';
49085         this.viewEl.select('img',true).each(function(e,i,n)  {
49086             val += e.is(".x-menu-item-checked") ? String(n) : '';
49087         });
49088         this.setValue(val, true);
49089     },
49090
49091     /**
49092      * Sets the checked state of the checkbox.
49093      * On is always based on a string comparison between inputValue and the param.
49094      * @param {Boolean/String} value - the value to set 
49095      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49096      */
49097     setValue : function(v,suppressEvent){
49098         if (!this.el.dom) {
49099             return;
49100         }
49101         var old = this.el.dom.value ;
49102         this.el.dom.value = v;
49103         if (suppressEvent) {
49104             return ;
49105         }
49106          
49107         // update display..
49108         this.viewEl.select('img',true).each(function(e,i,n)  {
49109             
49110             var on = e.is(".x-menu-item-checked");
49111             var newv = v.indexOf(String(n)) > -1;
49112             if (on != newv) {
49113                 e.toggleClass('x-menu-item-checked');
49114             }
49115             
49116         });
49117         
49118         
49119         this.fireEvent('change', this, v, old);
49120         
49121         
49122     },
49123    
49124     // handle setting of hidden value by some other method!!?!?
49125     setFromHidden: function()
49126     {
49127         if(!this.el){
49128             return;
49129         }
49130         //console.log("SET FROM HIDDEN");
49131         //alert('setFrom hidden');
49132         this.setValue(this.el.dom.value);
49133     },
49134     
49135     onDestroy : function()
49136     {
49137         if(this.viewEl){
49138             Roo.get(this.viewEl).remove();
49139         }
49140          
49141         Roo.form.DayPicker.superclass.onDestroy.call(this);
49142     }
49143
49144 });/*
49145  * RooJS Library 1.1.1
49146  * Copyright(c) 2008-2011  Alan Knowles
49147  *
49148  * License - LGPL
49149  */
49150  
49151
49152 /**
49153  * @class Roo.form.ComboCheck
49154  * @extends Roo.form.ComboBox
49155  * A combobox for multiple select items.
49156  *
49157  * FIXME - could do with a reset button..
49158  * 
49159  * @constructor
49160  * Create a new ComboCheck
49161  * @param {Object} config Configuration options
49162  */
49163 Roo.form.ComboCheck = function(config){
49164     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49165     // should verify some data...
49166     // like
49167     // hiddenName = required..
49168     // displayField = required
49169     // valudField == required
49170     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49171     var _t = this;
49172     Roo.each(req, function(e) {
49173         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49174             throw "Roo.form.ComboCheck : missing value for: " + e;
49175         }
49176     });
49177     
49178     
49179 };
49180
49181 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49182      
49183      
49184     editable : false,
49185      
49186     selectedClass: 'x-menu-item-checked', 
49187     
49188     // private
49189     onRender : function(ct, position){
49190         var _t = this;
49191         
49192         
49193         
49194         if(!this.tpl){
49195             var cls = 'x-combo-list';
49196
49197             
49198             this.tpl =  new Roo.Template({
49199                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49200                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49201                    '<span>{' + this.displayField + '}</span>' +
49202                     '</div>' 
49203                 
49204             });
49205         }
49206  
49207         
49208         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49209         this.view.singleSelect = false;
49210         this.view.multiSelect = true;
49211         this.view.toggleSelect = true;
49212         this.pageTb.add(new Roo.Toolbar.Fill(), {
49213             
49214             text: 'Done',
49215             handler: function()
49216             {
49217                 _t.collapse();
49218             }
49219         });
49220     },
49221     
49222     onViewOver : function(e, t){
49223         // do nothing...
49224         return;
49225         
49226     },
49227     
49228     onViewClick : function(doFocus,index){
49229         return;
49230         
49231     },
49232     select: function () {
49233         //Roo.log("SELECT CALLED");
49234     },
49235      
49236     selectByValue : function(xv, scrollIntoView){
49237         var ar = this.getValueArray();
49238         var sels = [];
49239         
49240         Roo.each(ar, function(v) {
49241             if(v === undefined || v === null){
49242                 return;
49243             }
49244             var r = this.findRecord(this.valueField, v);
49245             if(r){
49246                 sels.push(this.store.indexOf(r))
49247                 
49248             }
49249         },this);
49250         this.view.select(sels);
49251         return false;
49252     },
49253     
49254     
49255     
49256     onSelect : function(record, index){
49257        // Roo.log("onselect Called");
49258        // this is only called by the clear button now..
49259         this.view.clearSelections();
49260         this.setValue('[]');
49261         if (this.value != this.valueBefore) {
49262             this.fireEvent('change', this, this.value, this.valueBefore);
49263             this.valueBefore = this.value;
49264         }
49265     },
49266     getValueArray : function()
49267     {
49268         var ar = [] ;
49269         
49270         try {
49271             //Roo.log(this.value);
49272             if (typeof(this.value) == 'undefined') {
49273                 return [];
49274             }
49275             var ar = Roo.decode(this.value);
49276             return  ar instanceof Array ? ar : []; //?? valid?
49277             
49278         } catch(e) {
49279             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49280             return [];
49281         }
49282          
49283     },
49284     expand : function ()
49285     {
49286         
49287         Roo.form.ComboCheck.superclass.expand.call(this);
49288         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49289         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49290         
49291
49292     },
49293     
49294     collapse : function(){
49295         Roo.form.ComboCheck.superclass.collapse.call(this);
49296         var sl = this.view.getSelectedIndexes();
49297         var st = this.store;
49298         var nv = [];
49299         var tv = [];
49300         var r;
49301         Roo.each(sl, function(i) {
49302             r = st.getAt(i);
49303             nv.push(r.get(this.valueField));
49304         },this);
49305         this.setValue(Roo.encode(nv));
49306         if (this.value != this.valueBefore) {
49307
49308             this.fireEvent('change', this, this.value, this.valueBefore);
49309             this.valueBefore = this.value;
49310         }
49311         
49312     },
49313     
49314     setValue : function(v){
49315         // Roo.log(v);
49316         this.value = v;
49317         
49318         var vals = this.getValueArray();
49319         var tv = [];
49320         Roo.each(vals, function(k) {
49321             var r = this.findRecord(this.valueField, k);
49322             if(r){
49323                 tv.push(r.data[this.displayField]);
49324             }else if(this.valueNotFoundText !== undefined){
49325                 tv.push( this.valueNotFoundText );
49326             }
49327         },this);
49328        // Roo.log(tv);
49329         
49330         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49331         this.hiddenField.value = v;
49332         this.value = v;
49333     }
49334     
49335 });/*
49336  * Based on:
49337  * Ext JS Library 1.1.1
49338  * Copyright(c) 2006-2007, Ext JS, LLC.
49339  *
49340  * Originally Released Under LGPL - original licence link has changed is not relivant.
49341  *
49342  * Fork - LGPL
49343  * <script type="text/javascript">
49344  */
49345  
49346 /**
49347  * @class Roo.form.Signature
49348  * @extends Roo.form.Field
49349  * Signature field.  
49350  * @constructor
49351  * 
49352  * @param {Object} config Configuration options
49353  */
49354
49355 Roo.form.Signature = function(config){
49356     Roo.form.Signature.superclass.constructor.call(this, config);
49357     
49358     this.addEvents({// not in used??
49359          /**
49360          * @event confirm
49361          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49362              * @param {Roo.form.Signature} combo This combo box
49363              */
49364         'confirm' : true,
49365         /**
49366          * @event reset
49367          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49368              * @param {Roo.form.ComboBox} combo This combo box
49369              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49370              */
49371         'reset' : true
49372     });
49373 };
49374
49375 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49376     /**
49377      * @cfg {Object} labels Label to use when rendering a form.
49378      * defaults to 
49379      * labels : { 
49380      *      clear : "Clear",
49381      *      confirm : "Confirm"
49382      *  }
49383      */
49384     labels : { 
49385         clear : "Clear",
49386         confirm : "Confirm"
49387     },
49388     /**
49389      * @cfg {Number} width The signature panel width (defaults to 300)
49390      */
49391     width: 300,
49392     /**
49393      * @cfg {Number} height The signature panel height (defaults to 100)
49394      */
49395     height : 100,
49396     /**
49397      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49398      */
49399     allowBlank : false,
49400     
49401     //private
49402     // {Object} signPanel The signature SVG panel element (defaults to {})
49403     signPanel : {},
49404     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49405     isMouseDown : false,
49406     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49407     isConfirmed : false,
49408     // {String} signatureTmp SVG mapping string (defaults to empty string)
49409     signatureTmp : '',
49410     
49411     
49412     defaultAutoCreate : { // modified by initCompnoent..
49413         tag: "input",
49414         type:"hidden"
49415     },
49416
49417     // private
49418     onRender : function(ct, position){
49419         
49420         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49421         
49422         this.wrap = this.el.wrap({
49423             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49424         });
49425         
49426         this.createToolbar(this);
49427         this.signPanel = this.wrap.createChild({
49428                 tag: 'div',
49429                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49430             }, this.el
49431         );
49432             
49433         this.svgID = Roo.id();
49434         this.svgEl = this.signPanel.createChild({
49435               xmlns : 'http://www.w3.org/2000/svg',
49436               tag : 'svg',
49437               id : this.svgID + "-svg",
49438               width: this.width,
49439               height: this.height,
49440               viewBox: '0 0 '+this.width+' '+this.height,
49441               cn : [
49442                 {
49443                     tag: "rect",
49444                     id: this.svgID + "-svg-r",
49445                     width: this.width,
49446                     height: this.height,
49447                     fill: "#ffa"
49448                 },
49449                 {
49450                     tag: "line",
49451                     id: this.svgID + "-svg-l",
49452                     x1: "0", // start
49453                     y1: (this.height*0.8), // start set the line in 80% of height
49454                     x2: this.width, // end
49455                     y2: (this.height*0.8), // end set the line in 80% of height
49456                     'stroke': "#666",
49457                     'stroke-width': "1",
49458                     'stroke-dasharray': "3",
49459                     'shape-rendering': "crispEdges",
49460                     'pointer-events': "none"
49461                 },
49462                 {
49463                     tag: "path",
49464                     id: this.svgID + "-svg-p",
49465                     'stroke': "navy",
49466                     'stroke-width': "3",
49467                     'fill': "none",
49468                     'pointer-events': 'none'
49469                 }
49470               ]
49471         });
49472         this.createSVG();
49473         this.svgBox = this.svgEl.dom.getScreenCTM();
49474     },
49475     createSVG : function(){ 
49476         var svg = this.signPanel;
49477         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49478         var t = this;
49479
49480         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49481         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49482         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49483         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49484         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49485         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49486         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49487         
49488     },
49489     isTouchEvent : function(e){
49490         return e.type.match(/^touch/);
49491     },
49492     getCoords : function (e) {
49493         var pt    = this.svgEl.dom.createSVGPoint();
49494         pt.x = e.clientX; 
49495         pt.y = e.clientY;
49496         if (this.isTouchEvent(e)) {
49497             pt.x =  e.targetTouches[0].clientX;
49498             pt.y = e.targetTouches[0].clientY;
49499         }
49500         var a = this.svgEl.dom.getScreenCTM();
49501         var b = a.inverse();
49502         var mx = pt.matrixTransform(b);
49503         return mx.x + ',' + mx.y;
49504     },
49505     //mouse event headler 
49506     down : function (e) {
49507         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49508         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49509         
49510         this.isMouseDown = true;
49511         
49512         e.preventDefault();
49513     },
49514     move : function (e) {
49515         if (this.isMouseDown) {
49516             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49517             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49518         }
49519         
49520         e.preventDefault();
49521     },
49522     up : function (e) {
49523         this.isMouseDown = false;
49524         var sp = this.signatureTmp.split(' ');
49525         
49526         if(sp.length > 1){
49527             if(!sp[sp.length-2].match(/^L/)){
49528                 sp.pop();
49529                 sp.pop();
49530                 sp.push("");
49531                 this.signatureTmp = sp.join(" ");
49532             }
49533         }
49534         if(this.getValue() != this.signatureTmp){
49535             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49536             this.isConfirmed = false;
49537         }
49538         e.preventDefault();
49539     },
49540     
49541     /**
49542      * Protected method that will not generally be called directly. It
49543      * is called when the editor creates its toolbar. Override this method if you need to
49544      * add custom toolbar buttons.
49545      * @param {HtmlEditor} editor
49546      */
49547     createToolbar : function(editor){
49548          function btn(id, toggle, handler){
49549             var xid = fid + '-'+ id ;
49550             return {
49551                 id : xid,
49552                 cmd : id,
49553                 cls : 'x-btn-icon x-edit-'+id,
49554                 enableToggle:toggle !== false,
49555                 scope: editor, // was editor...
49556                 handler:handler||editor.relayBtnCmd,
49557                 clickEvent:'mousedown',
49558                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49559                 tabIndex:-1
49560             };
49561         }
49562         
49563         
49564         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49565         this.tb = tb;
49566         this.tb.add(
49567            {
49568                 cls : ' x-signature-btn x-signature-'+id,
49569                 scope: editor, // was editor...
49570                 handler: this.reset,
49571                 clickEvent:'mousedown',
49572                 text: this.labels.clear
49573             },
49574             {
49575                  xtype : 'Fill',
49576                  xns: Roo.Toolbar
49577             }, 
49578             {
49579                 cls : '  x-signature-btn x-signature-'+id,
49580                 scope: editor, // was editor...
49581                 handler: this.confirmHandler,
49582                 clickEvent:'mousedown',
49583                 text: this.labels.confirm
49584             }
49585         );
49586     
49587     },
49588     //public
49589     /**
49590      * when user is clicked confirm then show this image.....
49591      * 
49592      * @return {String} Image Data URI
49593      */
49594     getImageDataURI : function(){
49595         var svg = this.svgEl.dom.parentNode.innerHTML;
49596         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49597         return src; 
49598     },
49599     /**
49600      * 
49601      * @return {Boolean} this.isConfirmed
49602      */
49603     getConfirmed : function(){
49604         return this.isConfirmed;
49605     },
49606     /**
49607      * 
49608      * @return {Number} this.width
49609      */
49610     getWidth : function(){
49611         return this.width;
49612     },
49613     /**
49614      * 
49615      * @return {Number} this.height
49616      */
49617     getHeight : function(){
49618         return this.height;
49619     },
49620     // private
49621     getSignature : function(){
49622         return this.signatureTmp;
49623     },
49624     // private
49625     reset : function(){
49626         this.signatureTmp = '';
49627         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49628         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49629         this.isConfirmed = false;
49630         Roo.form.Signature.superclass.reset.call(this);
49631     },
49632     setSignature : function(s){
49633         this.signatureTmp = s;
49634         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49635         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49636         this.setValue(s);
49637         this.isConfirmed = false;
49638         Roo.form.Signature.superclass.reset.call(this);
49639     }, 
49640     test : function(){
49641 //        Roo.log(this.signPanel.dom.contentWindow.up())
49642     },
49643     //private
49644     setConfirmed : function(){
49645         
49646         
49647         
49648 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49649     },
49650     // private
49651     confirmHandler : function(){
49652         if(!this.getSignature()){
49653             return;
49654         }
49655         
49656         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49657         this.setValue(this.getSignature());
49658         this.isConfirmed = true;
49659         
49660         this.fireEvent('confirm', this);
49661     },
49662     // private
49663     // Subclasses should provide the validation implementation by overriding this
49664     validateValue : function(value){
49665         if(this.allowBlank){
49666             return true;
49667         }
49668         
49669         if(this.isConfirmed){
49670             return true;
49671         }
49672         return false;
49673     }
49674 });/*
49675  * Based on:
49676  * Ext JS Library 1.1.1
49677  * Copyright(c) 2006-2007, Ext JS, LLC.
49678  *
49679  * Originally Released Under LGPL - original licence link has changed is not relivant.
49680  *
49681  * Fork - LGPL
49682  * <script type="text/javascript">
49683  */
49684  
49685
49686 /**
49687  * @class Roo.form.ComboBox
49688  * @extends Roo.form.TriggerField
49689  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49690  * @constructor
49691  * Create a new ComboBox.
49692  * @param {Object} config Configuration options
49693  */
49694 Roo.form.Select = function(config){
49695     Roo.form.Select.superclass.constructor.call(this, config);
49696      
49697 };
49698
49699 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49700     /**
49701      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49702      */
49703     /**
49704      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49705      * rendering into an Roo.Editor, defaults to false)
49706      */
49707     /**
49708      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49709      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49710      */
49711     /**
49712      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49713      */
49714     /**
49715      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49716      * the dropdown list (defaults to undefined, with no header element)
49717      */
49718
49719      /**
49720      * @cfg {String/Roo.Template} tpl The template to use to render the output
49721      */
49722      
49723     // private
49724     defaultAutoCreate : {tag: "select"  },
49725     /**
49726      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49727      */
49728     listWidth: undefined,
49729     /**
49730      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49731      * mode = 'remote' or 'text' if mode = 'local')
49732      */
49733     displayField: undefined,
49734     /**
49735      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49736      * mode = 'remote' or 'value' if mode = 'local'). 
49737      * Note: use of a valueField requires the user make a selection
49738      * in order for a value to be mapped.
49739      */
49740     valueField: undefined,
49741     
49742     
49743     /**
49744      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49745      * field's data value (defaults to the underlying DOM element's name)
49746      */
49747     hiddenName: undefined,
49748     /**
49749      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49750      */
49751     listClass: '',
49752     /**
49753      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49754      */
49755     selectedClass: 'x-combo-selected',
49756     /**
49757      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49758      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49759      * which displays a downward arrow icon).
49760      */
49761     triggerClass : 'x-form-arrow-trigger',
49762     /**
49763      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49764      */
49765     shadow:'sides',
49766     /**
49767      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49768      * anchor positions (defaults to 'tl-bl')
49769      */
49770     listAlign: 'tl-bl?',
49771     /**
49772      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49773      */
49774     maxHeight: 300,
49775     /**
49776      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49777      * query specified by the allQuery config option (defaults to 'query')
49778      */
49779     triggerAction: 'query',
49780     /**
49781      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49782      * (defaults to 4, does not apply if editable = false)
49783      */
49784     minChars : 4,
49785     /**
49786      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49787      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49788      */
49789     typeAhead: false,
49790     /**
49791      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49792      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49793      */
49794     queryDelay: 500,
49795     /**
49796      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49797      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49798      */
49799     pageSize: 0,
49800     /**
49801      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49802      * when editable = true (defaults to false)
49803      */
49804     selectOnFocus:false,
49805     /**
49806      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49807      */
49808     queryParam: 'query',
49809     /**
49810      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49811      * when mode = 'remote' (defaults to 'Loading...')
49812      */
49813     loadingText: 'Loading...',
49814     /**
49815      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49816      */
49817     resizable: false,
49818     /**
49819      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49820      */
49821     handleHeight : 8,
49822     /**
49823      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49824      * traditional select (defaults to true)
49825      */
49826     editable: true,
49827     /**
49828      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49829      */
49830     allQuery: '',
49831     /**
49832      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49833      */
49834     mode: 'remote',
49835     /**
49836      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49837      * listWidth has a higher value)
49838      */
49839     minListWidth : 70,
49840     /**
49841      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49842      * allow the user to set arbitrary text into the field (defaults to false)
49843      */
49844     forceSelection:false,
49845     /**
49846      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49847      * if typeAhead = true (defaults to 250)
49848      */
49849     typeAheadDelay : 250,
49850     /**
49851      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49852      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49853      */
49854     valueNotFoundText : undefined,
49855     
49856     /**
49857      * @cfg {String} defaultValue The value displayed after loading the store.
49858      */
49859     defaultValue: '',
49860     
49861     /**
49862      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49863      */
49864     blockFocus : false,
49865     
49866     /**
49867      * @cfg {Boolean} disableClear Disable showing of clear button.
49868      */
49869     disableClear : false,
49870     /**
49871      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49872      */
49873     alwaysQuery : false,
49874     
49875     //private
49876     addicon : false,
49877     editicon: false,
49878     
49879     // element that contains real text value.. (when hidden is used..)
49880      
49881     // private
49882     onRender : function(ct, position){
49883         Roo.form.Field.prototype.onRender.call(this, ct, position);
49884         
49885         if(this.store){
49886             this.store.on('beforeload', this.onBeforeLoad, this);
49887             this.store.on('load', this.onLoad, this);
49888             this.store.on('loadexception', this.onLoadException, this);
49889             this.store.load({});
49890         }
49891         
49892         
49893         
49894     },
49895
49896     // private
49897     initEvents : function(){
49898         //Roo.form.ComboBox.superclass.initEvents.call(this);
49899  
49900     },
49901
49902     onDestroy : function(){
49903        
49904         if(this.store){
49905             this.store.un('beforeload', this.onBeforeLoad, this);
49906             this.store.un('load', this.onLoad, this);
49907             this.store.un('loadexception', this.onLoadException, this);
49908         }
49909         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49910     },
49911
49912     // private
49913     fireKey : function(e){
49914         if(e.isNavKeyPress() && !this.list.isVisible()){
49915             this.fireEvent("specialkey", this, e);
49916         }
49917     },
49918
49919     // private
49920     onResize: function(w, h){
49921         
49922         return; 
49923     
49924         
49925     },
49926
49927     /**
49928      * Allow or prevent the user from directly editing the field text.  If false is passed,
49929      * the user will only be able to select from the items defined in the dropdown list.  This method
49930      * is the runtime equivalent of setting the 'editable' config option at config time.
49931      * @param {Boolean} value True to allow the user to directly edit the field text
49932      */
49933     setEditable : function(value){
49934          
49935     },
49936
49937     // private
49938     onBeforeLoad : function(){
49939         
49940         Roo.log("Select before load");
49941         return;
49942     
49943         this.innerList.update(this.loadingText ?
49944                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49945         //this.restrictHeight();
49946         this.selectedIndex = -1;
49947     },
49948
49949     // private
49950     onLoad : function(){
49951
49952     
49953         var dom = this.el.dom;
49954         dom.innerHTML = '';
49955          var od = dom.ownerDocument;
49956          
49957         if (this.emptyText) {
49958             var op = od.createElement('option');
49959             op.setAttribute('value', '');
49960             op.innerHTML = String.format('{0}', this.emptyText);
49961             dom.appendChild(op);
49962         }
49963         if(this.store.getCount() > 0){
49964            
49965             var vf = this.valueField;
49966             var df = this.displayField;
49967             this.store.data.each(function(r) {
49968                 // which colmsn to use... testing - cdoe / title..
49969                 var op = od.createElement('option');
49970                 op.setAttribute('value', r.data[vf]);
49971                 op.innerHTML = String.format('{0}', r.data[df]);
49972                 dom.appendChild(op);
49973             });
49974             if (typeof(this.defaultValue != 'undefined')) {
49975                 this.setValue(this.defaultValue);
49976             }
49977             
49978              
49979         }else{
49980             //this.onEmptyResults();
49981         }
49982         //this.el.focus();
49983     },
49984     // private
49985     onLoadException : function()
49986     {
49987         dom.innerHTML = '';
49988             
49989         Roo.log("Select on load exception");
49990         return;
49991     
49992         this.collapse();
49993         Roo.log(this.store.reader.jsonData);
49994         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
49995             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
49996         }
49997         
49998         
49999     },
50000     // private
50001     onTypeAhead : function(){
50002          
50003     },
50004
50005     // private
50006     onSelect : function(record, index){
50007         Roo.log('on select?');
50008         return;
50009         if(this.fireEvent('beforeselect', this, record, index) !== false){
50010             this.setFromData(index > -1 ? record.data : false);
50011             this.collapse();
50012             this.fireEvent('select', this, record, index);
50013         }
50014     },
50015
50016     /**
50017      * Returns the currently selected field value or empty string if no value is set.
50018      * @return {String} value The selected value
50019      */
50020     getValue : function(){
50021         var dom = this.el.dom;
50022         this.value = dom.options[dom.selectedIndex].value;
50023         return this.value;
50024         
50025     },
50026
50027     /**
50028      * Clears any text/value currently set in the field
50029      */
50030     clearValue : function(){
50031         this.value = '';
50032         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50033         
50034     },
50035
50036     /**
50037      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50038      * will be displayed in the field.  If the value does not match the data value of an existing item,
50039      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50040      * Otherwise the field will be blank (although the value will still be set).
50041      * @param {String} value The value to match
50042      */
50043     setValue : function(v){
50044         var d = this.el.dom;
50045         for (var i =0; i < d.options.length;i++) {
50046             if (v == d.options[i].value) {
50047                 d.selectedIndex = i;
50048                 this.value = v;
50049                 return;
50050             }
50051         }
50052         this.clearValue();
50053     },
50054     /**
50055      * @property {Object} the last set data for the element
50056      */
50057     
50058     lastData : false,
50059     /**
50060      * Sets the value of the field based on a object which is related to the record format for the store.
50061      * @param {Object} value the value to set as. or false on reset?
50062      */
50063     setFromData : function(o){
50064         Roo.log('setfrom data?');
50065          
50066         
50067         
50068     },
50069     // private
50070     reset : function(){
50071         this.clearValue();
50072     },
50073     // private
50074     findRecord : function(prop, value){
50075         
50076         return false;
50077     
50078         var record;
50079         if(this.store.getCount() > 0){
50080             this.store.each(function(r){
50081                 if(r.data[prop] == value){
50082                     record = r;
50083                     return false;
50084                 }
50085                 return true;
50086             });
50087         }
50088         return record;
50089     },
50090     
50091     getName: function()
50092     {
50093         // returns hidden if it's set..
50094         if (!this.rendered) {return ''};
50095         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50096         
50097     },
50098      
50099
50100     
50101
50102     // private
50103     onEmptyResults : function(){
50104         Roo.log('empty results');
50105         //this.collapse();
50106     },
50107
50108     /**
50109      * Returns true if the dropdown list is expanded, else false.
50110      */
50111     isExpanded : function(){
50112         return false;
50113     },
50114
50115     /**
50116      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50117      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50118      * @param {String} value The data value of the item to select
50119      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50120      * selected item if it is not currently in view (defaults to true)
50121      * @return {Boolean} True if the value matched an item in the list, else false
50122      */
50123     selectByValue : function(v, scrollIntoView){
50124         Roo.log('select By Value');
50125         return false;
50126     
50127         if(v !== undefined && v !== null){
50128             var r = this.findRecord(this.valueField || this.displayField, v);
50129             if(r){
50130                 this.select(this.store.indexOf(r), scrollIntoView);
50131                 return true;
50132             }
50133         }
50134         return false;
50135     },
50136
50137     /**
50138      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50139      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50140      * @param {Number} index The zero-based index of the list item to select
50141      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50142      * selected item if it is not currently in view (defaults to true)
50143      */
50144     select : function(index, scrollIntoView){
50145         Roo.log('select ');
50146         return  ;
50147         
50148         this.selectedIndex = index;
50149         this.view.select(index);
50150         if(scrollIntoView !== false){
50151             var el = this.view.getNode(index);
50152             if(el){
50153                 this.innerList.scrollChildIntoView(el, false);
50154             }
50155         }
50156     },
50157
50158       
50159
50160     // private
50161     validateBlur : function(){
50162         
50163         return;
50164         
50165     },
50166
50167     // private
50168     initQuery : function(){
50169         this.doQuery(this.getRawValue());
50170     },
50171
50172     // private
50173     doForce : function(){
50174         if(this.el.dom.value.length > 0){
50175             this.el.dom.value =
50176                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50177              
50178         }
50179     },
50180
50181     /**
50182      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50183      * query allowing the query action to be canceled if needed.
50184      * @param {String} query The SQL query to execute
50185      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50186      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50187      * saved in the current store (defaults to false)
50188      */
50189     doQuery : function(q, forceAll){
50190         
50191         Roo.log('doQuery?');
50192         if(q === undefined || q === null){
50193             q = '';
50194         }
50195         var qe = {
50196             query: q,
50197             forceAll: forceAll,
50198             combo: this,
50199             cancel:false
50200         };
50201         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50202             return false;
50203         }
50204         q = qe.query;
50205         forceAll = qe.forceAll;
50206         if(forceAll === true || (q.length >= this.minChars)){
50207             if(this.lastQuery != q || this.alwaysQuery){
50208                 this.lastQuery = q;
50209                 if(this.mode == 'local'){
50210                     this.selectedIndex = -1;
50211                     if(forceAll){
50212                         this.store.clearFilter();
50213                     }else{
50214                         this.store.filter(this.displayField, q);
50215                     }
50216                     this.onLoad();
50217                 }else{
50218                     this.store.baseParams[this.queryParam] = q;
50219                     this.store.load({
50220                         params: this.getParams(q)
50221                     });
50222                     this.expand();
50223                 }
50224             }else{
50225                 this.selectedIndex = -1;
50226                 this.onLoad();   
50227             }
50228         }
50229     },
50230
50231     // private
50232     getParams : function(q){
50233         var p = {};
50234         //p[this.queryParam] = q;
50235         if(this.pageSize){
50236             p.start = 0;
50237             p.limit = this.pageSize;
50238         }
50239         return p;
50240     },
50241
50242     /**
50243      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50244      */
50245     collapse : function(){
50246         
50247     },
50248
50249     // private
50250     collapseIf : function(e){
50251         
50252     },
50253
50254     /**
50255      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50256      */
50257     expand : function(){
50258         
50259     } ,
50260
50261     // private
50262      
50263
50264     /** 
50265     * @cfg {Boolean} grow 
50266     * @hide 
50267     */
50268     /** 
50269     * @cfg {Number} growMin 
50270     * @hide 
50271     */
50272     /** 
50273     * @cfg {Number} growMax 
50274     * @hide 
50275     */
50276     /**
50277      * @hide
50278      * @method autoSize
50279      */
50280     
50281     setWidth : function()
50282     {
50283         
50284     },
50285     getResizeEl : function(){
50286         return this.el;
50287     }
50288 });//<script type="text/javasscript">
50289  
50290
50291 /**
50292  * @class Roo.DDView
50293  * A DnD enabled version of Roo.View.
50294  * @param {Element/String} container The Element in which to create the View.
50295  * @param {String} tpl The template string used to create the markup for each element of the View
50296  * @param {Object} config The configuration properties. These include all the config options of
50297  * {@link Roo.View} plus some specific to this class.<br>
50298  * <p>
50299  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50300  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50301  * <p>
50302  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50303 .x-view-drag-insert-above {
50304         border-top:1px dotted #3366cc;
50305 }
50306 .x-view-drag-insert-below {
50307         border-bottom:1px dotted #3366cc;
50308 }
50309 </code></pre>
50310  * 
50311  */
50312  
50313 Roo.DDView = function(container, tpl, config) {
50314     Roo.DDView.superclass.constructor.apply(this, arguments);
50315     this.getEl().setStyle("outline", "0px none");
50316     this.getEl().unselectable();
50317     if (this.dragGroup) {
50318                 this.setDraggable(this.dragGroup.split(","));
50319     }
50320     if (this.dropGroup) {
50321                 this.setDroppable(this.dropGroup.split(","));
50322     }
50323     if (this.deletable) {
50324         this.setDeletable();
50325     }
50326     this.isDirtyFlag = false;
50327         this.addEvents({
50328                 "drop" : true
50329         });
50330 };
50331
50332 Roo.extend(Roo.DDView, Roo.View, {
50333 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50334 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50335 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50336 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50337
50338         isFormField: true,
50339
50340         reset: Roo.emptyFn,
50341         
50342         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50343
50344         validate: function() {
50345                 return true;
50346         },
50347         
50348         destroy: function() {
50349                 this.purgeListeners();
50350                 this.getEl.removeAllListeners();
50351                 this.getEl().remove();
50352                 if (this.dragZone) {
50353                         if (this.dragZone.destroy) {
50354                                 this.dragZone.destroy();
50355                         }
50356                 }
50357                 if (this.dropZone) {
50358                         if (this.dropZone.destroy) {
50359                                 this.dropZone.destroy();
50360                         }
50361                 }
50362         },
50363
50364 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50365         getName: function() {
50366                 return this.name;
50367         },
50368
50369 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50370         setValue: function(v) {
50371                 if (!this.store) {
50372                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50373                 }
50374                 var data = {};
50375                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50376                 this.store.proxy = new Roo.data.MemoryProxy(data);
50377                 this.store.load();
50378         },
50379
50380 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50381         getValue: function() {
50382                 var result = '(';
50383                 this.store.each(function(rec) {
50384                         result += rec.id + ',';
50385                 });
50386                 return result.substr(0, result.length - 1) + ')';
50387         },
50388         
50389         getIds: function() {
50390                 var i = 0, result = new Array(this.store.getCount());
50391                 this.store.each(function(rec) {
50392                         result[i++] = rec.id;
50393                 });
50394                 return result;
50395         },
50396         
50397         isDirty: function() {
50398                 return this.isDirtyFlag;
50399         },
50400
50401 /**
50402  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50403  *      whole Element becomes the target, and this causes the drop gesture to append.
50404  */
50405     getTargetFromEvent : function(e) {
50406                 var target = e.getTarget();
50407                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50408                 target = target.parentNode;
50409                 }
50410                 if (!target) {
50411                         target = this.el.dom.lastChild || this.el.dom;
50412                 }
50413                 return target;
50414     },
50415
50416 /**
50417  *      Create the drag data which consists of an object which has the property "ddel" as
50418  *      the drag proxy element. 
50419  */
50420     getDragData : function(e) {
50421         var target = this.findItemFromChild(e.getTarget());
50422                 if(target) {
50423                         this.handleSelection(e);
50424                         var selNodes = this.getSelectedNodes();
50425             var dragData = {
50426                 source: this,
50427                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50428                 nodes: selNodes,
50429                 records: []
50430                         };
50431                         var selectedIndices = this.getSelectedIndexes();
50432                         for (var i = 0; i < selectedIndices.length; i++) {
50433                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50434                         }
50435                         if (selNodes.length == 1) {
50436                                 dragData.ddel = target.cloneNode(true); // the div element
50437                         } else {
50438                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50439                                 div.className = 'multi-proxy';
50440                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50441                                         div.appendChild(selNodes[i].cloneNode(true));
50442                                 }
50443                                 dragData.ddel = div;
50444                         }
50445             //console.log(dragData)
50446             //console.log(dragData.ddel.innerHTML)
50447                         return dragData;
50448                 }
50449         //console.log('nodragData')
50450                 return false;
50451     },
50452     
50453 /**     Specify to which ddGroup items in this DDView may be dragged. */
50454     setDraggable: function(ddGroup) {
50455         if (ddGroup instanceof Array) {
50456                 Roo.each(ddGroup, this.setDraggable, this);
50457                 return;
50458         }
50459         if (this.dragZone) {
50460                 this.dragZone.addToGroup(ddGroup);
50461         } else {
50462                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50463                                 containerScroll: true,
50464                                 ddGroup: ddGroup 
50465
50466                         });
50467 //                      Draggability implies selection. DragZone's mousedown selects the element.
50468                         if (!this.multiSelect) { this.singleSelect = true; }
50469
50470 //                      Wire the DragZone's handlers up to methods in *this*
50471                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50472                 }
50473     },
50474
50475 /**     Specify from which ddGroup this DDView accepts drops. */
50476     setDroppable: function(ddGroup) {
50477         if (ddGroup instanceof Array) {
50478                 Roo.each(ddGroup, this.setDroppable, this);
50479                 return;
50480         }
50481         if (this.dropZone) {
50482                 this.dropZone.addToGroup(ddGroup);
50483         } else {
50484                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50485                                 containerScroll: true,
50486                                 ddGroup: ddGroup
50487                         });
50488
50489 //                      Wire the DropZone's handlers up to methods in *this*
50490                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50491                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50492                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50493                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50494                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50495                 }
50496     },
50497
50498 /**     Decide whether to drop above or below a View node. */
50499     getDropPoint : function(e, n, dd){
50500         if (n == this.el.dom) { return "above"; }
50501                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50502                 var c = t + (b - t) / 2;
50503                 var y = Roo.lib.Event.getPageY(e);
50504                 if(y <= c) {
50505                         return "above";
50506                 }else{
50507                         return "below";
50508                 }
50509     },
50510
50511     onNodeEnter : function(n, dd, e, data){
50512                 return false;
50513     },
50514     
50515     onNodeOver : function(n, dd, e, data){
50516                 var pt = this.getDropPoint(e, n, dd);
50517                 // set the insert point style on the target node
50518                 var dragElClass = this.dropNotAllowed;
50519                 if (pt) {
50520                         var targetElClass;
50521                         if (pt == "above"){
50522                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50523                                 targetElClass = "x-view-drag-insert-above";
50524                         } else {
50525                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50526                                 targetElClass = "x-view-drag-insert-below";
50527                         }
50528                         if (this.lastInsertClass != targetElClass){
50529                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50530                                 this.lastInsertClass = targetElClass;
50531                         }
50532                 }
50533                 return dragElClass;
50534         },
50535
50536     onNodeOut : function(n, dd, e, data){
50537                 this.removeDropIndicators(n);
50538     },
50539
50540     onNodeDrop : function(n, dd, e, data){
50541         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50542                 return false;
50543         }
50544         var pt = this.getDropPoint(e, n, dd);
50545                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50546                 if (pt == "below") { insertAt++; }
50547                 for (var i = 0; i < data.records.length; i++) {
50548                         var r = data.records[i];
50549                         var dup = this.store.getById(r.id);
50550                         if (dup && (dd != this.dragZone)) {
50551                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50552                         } else {
50553                                 if (data.copy) {
50554                                         this.store.insert(insertAt++, r.copy());
50555                                 } else {
50556                                         data.source.isDirtyFlag = true;
50557                                         r.store.remove(r);
50558                                         this.store.insert(insertAt++, r);
50559                                 }
50560                                 this.isDirtyFlag = true;
50561                         }
50562                 }
50563                 this.dragZone.cachedTarget = null;
50564                 return true;
50565     },
50566
50567     removeDropIndicators : function(n){
50568                 if(n){
50569                         Roo.fly(n).removeClass([
50570                                 "x-view-drag-insert-above",
50571                                 "x-view-drag-insert-below"]);
50572                         this.lastInsertClass = "_noclass";
50573                 }
50574     },
50575
50576 /**
50577  *      Utility method. Add a delete option to the DDView's context menu.
50578  *      @param {String} imageUrl The URL of the "delete" icon image.
50579  */
50580         setDeletable: function(imageUrl) {
50581                 if (!this.singleSelect && !this.multiSelect) {
50582                         this.singleSelect = true;
50583                 }
50584                 var c = this.getContextMenu();
50585                 this.contextMenu.on("itemclick", function(item) {
50586                         switch (item.id) {
50587                                 case "delete":
50588                                         this.remove(this.getSelectedIndexes());
50589                                         break;
50590                         }
50591                 }, this);
50592                 this.contextMenu.add({
50593                         icon: imageUrl,
50594                         id: "delete",
50595                         text: 'Delete'
50596                 });
50597         },
50598         
50599 /**     Return the context menu for this DDView. */
50600         getContextMenu: function() {
50601                 if (!this.contextMenu) {
50602 //                      Create the View's context menu
50603                         this.contextMenu = new Roo.menu.Menu({
50604                                 id: this.id + "-contextmenu"
50605                         });
50606                         this.el.on("contextmenu", this.showContextMenu, this);
50607                 }
50608                 return this.contextMenu;
50609         },
50610         
50611         disableContextMenu: function() {
50612                 if (this.contextMenu) {
50613                         this.el.un("contextmenu", this.showContextMenu, this);
50614                 }
50615         },
50616
50617         showContextMenu: function(e, item) {
50618         item = this.findItemFromChild(e.getTarget());
50619                 if (item) {
50620                         e.stopEvent();
50621                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50622                         this.contextMenu.showAt(e.getXY());
50623             }
50624     },
50625
50626 /**
50627  *      Remove {@link Roo.data.Record}s at the specified indices.
50628  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50629  */
50630     remove: function(selectedIndices) {
50631                 selectedIndices = [].concat(selectedIndices);
50632                 for (var i = 0; i < selectedIndices.length; i++) {
50633                         var rec = this.store.getAt(selectedIndices[i]);
50634                         this.store.remove(rec);
50635                 }
50636     },
50637
50638 /**
50639  *      Double click fires the event, but also, if this is draggable, and there is only one other
50640  *      related DropZone, it transfers the selected node.
50641  */
50642     onDblClick : function(e){
50643         var item = this.findItemFromChild(e.getTarget());
50644         if(item){
50645             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50646                 return false;
50647             }
50648             if (this.dragGroup) {
50649                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50650                     while (targets.indexOf(this.dropZone) > -1) {
50651                             targets.remove(this.dropZone);
50652                                 }
50653                     if (targets.length == 1) {
50654                                         this.dragZone.cachedTarget = null;
50655                         var el = Roo.get(targets[0].getEl());
50656                         var box = el.getBox(true);
50657                         targets[0].onNodeDrop(el.dom, {
50658                                 target: el.dom,
50659                                 xy: [box.x, box.y + box.height - 1]
50660                         }, null, this.getDragData(e));
50661                     }
50662                 }
50663         }
50664     },
50665     
50666     handleSelection: function(e) {
50667                 this.dragZone.cachedTarget = null;
50668         var item = this.findItemFromChild(e.getTarget());
50669         if (!item) {
50670                 this.clearSelections(true);
50671                 return;
50672         }
50673                 if (item && (this.multiSelect || this.singleSelect)){
50674                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50675                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50676                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50677                                 this.unselect(item);
50678                         } else {
50679                                 this.select(item, this.multiSelect && e.ctrlKey);
50680                                 this.lastSelection = item;
50681                         }
50682                 }
50683     },
50684
50685     onItemClick : function(item, index, e){
50686                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50687                         return false;
50688                 }
50689                 return true;
50690     },
50691
50692     unselect : function(nodeInfo, suppressEvent){
50693                 var node = this.getNode(nodeInfo);
50694                 if(node && this.isSelected(node)){
50695                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50696                                 Roo.fly(node).removeClass(this.selectedClass);
50697                                 this.selections.remove(node);
50698                                 if(!suppressEvent){
50699                                         this.fireEvent("selectionchange", this, this.selections);
50700                                 }
50701                         }
50702                 }
50703     }
50704 });
50705 /*
50706  * Based on:
50707  * Ext JS Library 1.1.1
50708  * Copyright(c) 2006-2007, Ext JS, LLC.
50709  *
50710  * Originally Released Under LGPL - original licence link has changed is not relivant.
50711  *
50712  * Fork - LGPL
50713  * <script type="text/javascript">
50714  */
50715  
50716 /**
50717  * @class Roo.LayoutManager
50718  * @extends Roo.util.Observable
50719  * Base class for layout managers.
50720  */
50721 Roo.LayoutManager = function(container, config){
50722     Roo.LayoutManager.superclass.constructor.call(this);
50723     this.el = Roo.get(container);
50724     // ie scrollbar fix
50725     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50726         document.body.scroll = "no";
50727     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50728         this.el.position('relative');
50729     }
50730     this.id = this.el.id;
50731     this.el.addClass("x-layout-container");
50732     /** false to disable window resize monitoring @type Boolean */
50733     this.monitorWindowResize = true;
50734     this.regions = {};
50735     this.addEvents({
50736         /**
50737          * @event layout
50738          * Fires when a layout is performed. 
50739          * @param {Roo.LayoutManager} this
50740          */
50741         "layout" : true,
50742         /**
50743          * @event regionresized
50744          * Fires when the user resizes a region. 
50745          * @param {Roo.LayoutRegion} region The resized region
50746          * @param {Number} newSize The new size (width for east/west, height for north/south)
50747          */
50748         "regionresized" : true,
50749         /**
50750          * @event regioncollapsed
50751          * Fires when a region is collapsed. 
50752          * @param {Roo.LayoutRegion} region The collapsed region
50753          */
50754         "regioncollapsed" : true,
50755         /**
50756          * @event regionexpanded
50757          * Fires when a region is expanded.  
50758          * @param {Roo.LayoutRegion} region The expanded region
50759          */
50760         "regionexpanded" : true
50761     });
50762     this.updating = false;
50763     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50764 };
50765
50766 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50767     /**
50768      * Returns true if this layout is currently being updated
50769      * @return {Boolean}
50770      */
50771     isUpdating : function(){
50772         return this.updating; 
50773     },
50774     
50775     /**
50776      * Suspend the LayoutManager from doing auto-layouts while
50777      * making multiple add or remove calls
50778      */
50779     beginUpdate : function(){
50780         this.updating = true;    
50781     },
50782     
50783     /**
50784      * Restore auto-layouts and optionally disable the manager from performing a layout
50785      * @param {Boolean} noLayout true to disable a layout update 
50786      */
50787     endUpdate : function(noLayout){
50788         this.updating = false;
50789         if(!noLayout){
50790             this.layout();
50791         }    
50792     },
50793     
50794     layout: function(){
50795         
50796     },
50797     
50798     onRegionResized : function(region, newSize){
50799         this.fireEvent("regionresized", region, newSize);
50800         this.layout();
50801     },
50802     
50803     onRegionCollapsed : function(region){
50804         this.fireEvent("regioncollapsed", region);
50805     },
50806     
50807     onRegionExpanded : function(region){
50808         this.fireEvent("regionexpanded", region);
50809     },
50810         
50811     /**
50812      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50813      * performs box-model adjustments.
50814      * @return {Object} The size as an object {width: (the width), height: (the height)}
50815      */
50816     getViewSize : function(){
50817         var size;
50818         if(this.el.dom != document.body){
50819             size = this.el.getSize();
50820         }else{
50821             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50822         }
50823         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50824         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50825         return size;
50826     },
50827     
50828     /**
50829      * Returns the Element this layout is bound to.
50830      * @return {Roo.Element}
50831      */
50832     getEl : function(){
50833         return this.el;
50834     },
50835     
50836     /**
50837      * Returns the specified region.
50838      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50839      * @return {Roo.LayoutRegion}
50840      */
50841     getRegion : function(target){
50842         return this.regions[target.toLowerCase()];
50843     },
50844     
50845     onWindowResize : function(){
50846         if(this.monitorWindowResize){
50847             this.layout();
50848         }
50849     }
50850 });/*
50851  * Based on:
50852  * Ext JS Library 1.1.1
50853  * Copyright(c) 2006-2007, Ext JS, LLC.
50854  *
50855  * Originally Released Under LGPL - original licence link has changed is not relivant.
50856  *
50857  * Fork - LGPL
50858  * <script type="text/javascript">
50859  */
50860 /**
50861  * @class Roo.BorderLayout
50862  * @extends Roo.LayoutManager
50863  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50864  * please see: <br><br>
50865  * <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>
50866  * <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>
50867  * Example:
50868  <pre><code>
50869  var layout = new Roo.BorderLayout(document.body, {
50870     north: {
50871         initialSize: 25,
50872         titlebar: false
50873     },
50874     west: {
50875         split:true,
50876         initialSize: 200,
50877         minSize: 175,
50878         maxSize: 400,
50879         titlebar: true,
50880         collapsible: true
50881     },
50882     east: {
50883         split:true,
50884         initialSize: 202,
50885         minSize: 175,
50886         maxSize: 400,
50887         titlebar: true,
50888         collapsible: true
50889     },
50890     south: {
50891         split:true,
50892         initialSize: 100,
50893         minSize: 100,
50894         maxSize: 200,
50895         titlebar: true,
50896         collapsible: true
50897     },
50898     center: {
50899         titlebar: true,
50900         autoScroll:true,
50901         resizeTabs: true,
50902         minTabWidth: 50,
50903         preferredTabWidth: 150
50904     }
50905 });
50906
50907 // shorthand
50908 var CP = Roo.ContentPanel;
50909
50910 layout.beginUpdate();
50911 layout.add("north", new CP("north", "North"));
50912 layout.add("south", new CP("south", {title: "South", closable: true}));
50913 layout.add("west", new CP("west", {title: "West"}));
50914 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50915 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50916 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50917 layout.getRegion("center").showPanel("center1");
50918 layout.endUpdate();
50919 </code></pre>
50920
50921 <b>The container the layout is rendered into can be either the body element or any other element.
50922 If it is not the body element, the container needs to either be an absolute positioned element,
50923 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50924 the container size if it is not the body element.</b>
50925
50926 * @constructor
50927 * Create a new BorderLayout
50928 * @param {String/HTMLElement/Element} container The container this layout is bound to
50929 * @param {Object} config Configuration options
50930  */
50931 Roo.BorderLayout = function(container, config){
50932     config = config || {};
50933     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50934     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50935     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50936         var target = this.factory.validRegions[i];
50937         if(config[target]){
50938             this.addRegion(target, config[target]);
50939         }
50940     }
50941 };
50942
50943 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50944     /**
50945      * Creates and adds a new region if it doesn't already exist.
50946      * @param {String} target The target region key (north, south, east, west or center).
50947      * @param {Object} config The regions config object
50948      * @return {BorderLayoutRegion} The new region
50949      */
50950     addRegion : function(target, config){
50951         if(!this.regions[target]){
50952             var r = this.factory.create(target, this, config);
50953             this.bindRegion(target, r);
50954         }
50955         return this.regions[target];
50956     },
50957
50958     // private (kinda)
50959     bindRegion : function(name, r){
50960         this.regions[name] = r;
50961         r.on("visibilitychange", this.layout, this);
50962         r.on("paneladded", this.layout, this);
50963         r.on("panelremoved", this.layout, this);
50964         r.on("invalidated", this.layout, this);
50965         r.on("resized", this.onRegionResized, this);
50966         r.on("collapsed", this.onRegionCollapsed, this);
50967         r.on("expanded", this.onRegionExpanded, this);
50968     },
50969
50970     /**
50971      * Performs a layout update.
50972      */
50973     layout : function(){
50974         if(this.updating) {
50975             return;
50976         }
50977         var size = this.getViewSize();
50978         var w = size.width;
50979         var h = size.height;
50980         var centerW = w;
50981         var centerH = h;
50982         var centerY = 0;
50983         var centerX = 0;
50984         //var x = 0, y = 0;
50985
50986         var rs = this.regions;
50987         var north = rs["north"];
50988         var south = rs["south"]; 
50989         var west = rs["west"];
50990         var east = rs["east"];
50991         var center = rs["center"];
50992         //if(this.hideOnLayout){ // not supported anymore
50993             //c.el.setStyle("display", "none");
50994         //}
50995         if(north && north.isVisible()){
50996             var b = north.getBox();
50997             var m = north.getMargins();
50998             b.width = w - (m.left+m.right);
50999             b.x = m.left;
51000             b.y = m.top;
51001             centerY = b.height + b.y + m.bottom;
51002             centerH -= centerY;
51003             north.updateBox(this.safeBox(b));
51004         }
51005         if(south && south.isVisible()){
51006             var b = south.getBox();
51007             var m = south.getMargins();
51008             b.width = w - (m.left+m.right);
51009             b.x = m.left;
51010             var totalHeight = (b.height + m.top + m.bottom);
51011             b.y = h - totalHeight + m.top;
51012             centerH -= totalHeight;
51013             south.updateBox(this.safeBox(b));
51014         }
51015         if(west && west.isVisible()){
51016             var b = west.getBox();
51017             var m = west.getMargins();
51018             b.height = centerH - (m.top+m.bottom);
51019             b.x = m.left;
51020             b.y = centerY + m.top;
51021             var totalWidth = (b.width + m.left + m.right);
51022             centerX += totalWidth;
51023             centerW -= totalWidth;
51024             west.updateBox(this.safeBox(b));
51025         }
51026         if(east && east.isVisible()){
51027             var b = east.getBox();
51028             var m = east.getMargins();
51029             b.height = centerH - (m.top+m.bottom);
51030             var totalWidth = (b.width + m.left + m.right);
51031             b.x = w - totalWidth + m.left;
51032             b.y = centerY + m.top;
51033             centerW -= totalWidth;
51034             east.updateBox(this.safeBox(b));
51035         }
51036         if(center){
51037             var m = center.getMargins();
51038             var centerBox = {
51039                 x: centerX + m.left,
51040                 y: centerY + m.top,
51041                 width: centerW - (m.left+m.right),
51042                 height: centerH - (m.top+m.bottom)
51043             };
51044             //if(this.hideOnLayout){
51045                 //center.el.setStyle("display", "block");
51046             //}
51047             center.updateBox(this.safeBox(centerBox));
51048         }
51049         this.el.repaint();
51050         this.fireEvent("layout", this);
51051     },
51052
51053     // private
51054     safeBox : function(box){
51055         box.width = Math.max(0, box.width);
51056         box.height = Math.max(0, box.height);
51057         return box;
51058     },
51059
51060     /**
51061      * Adds a ContentPanel (or subclass) to this layout.
51062      * @param {String} target The target region key (north, south, east, west or center).
51063      * @param {Roo.ContentPanel} panel The panel to add
51064      * @return {Roo.ContentPanel} The added panel
51065      */
51066     add : function(target, panel){
51067          
51068         target = target.toLowerCase();
51069         return this.regions[target].add(panel);
51070     },
51071
51072     /**
51073      * Remove a ContentPanel (or subclass) to this layout.
51074      * @param {String} target The target region key (north, south, east, west or center).
51075      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51076      * @return {Roo.ContentPanel} The removed panel
51077      */
51078     remove : function(target, panel){
51079         target = target.toLowerCase();
51080         return this.regions[target].remove(panel);
51081     },
51082
51083     /**
51084      * Searches all regions for a panel with the specified id
51085      * @param {String} panelId
51086      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51087      */
51088     findPanel : function(panelId){
51089         var rs = this.regions;
51090         for(var target in rs){
51091             if(typeof rs[target] != "function"){
51092                 var p = rs[target].getPanel(panelId);
51093                 if(p){
51094                     return p;
51095                 }
51096             }
51097         }
51098         return null;
51099     },
51100
51101     /**
51102      * Searches all regions for a panel with the specified id and activates (shows) it.
51103      * @param {String/ContentPanel} panelId The panels id or the panel itself
51104      * @return {Roo.ContentPanel} The shown panel or null
51105      */
51106     showPanel : function(panelId) {
51107       var rs = this.regions;
51108       for(var target in rs){
51109          var r = rs[target];
51110          if(typeof r != "function"){
51111             if(r.hasPanel(panelId)){
51112                return r.showPanel(panelId);
51113             }
51114          }
51115       }
51116       return null;
51117    },
51118
51119    /**
51120      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51121      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51122      */
51123     restoreState : function(provider){
51124         if(!provider){
51125             provider = Roo.state.Manager;
51126         }
51127         var sm = new Roo.LayoutStateManager();
51128         sm.init(this, provider);
51129     },
51130
51131     /**
51132      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51133      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51134      * a valid ContentPanel config object.  Example:
51135      * <pre><code>
51136 // Create the main layout
51137 var layout = new Roo.BorderLayout('main-ct', {
51138     west: {
51139         split:true,
51140         minSize: 175,
51141         titlebar: true
51142     },
51143     center: {
51144         title:'Components'
51145     }
51146 }, 'main-ct');
51147
51148 // Create and add multiple ContentPanels at once via configs
51149 layout.batchAdd({
51150    west: {
51151        id: 'source-files',
51152        autoCreate:true,
51153        title:'Ext Source Files',
51154        autoScroll:true,
51155        fitToFrame:true
51156    },
51157    center : {
51158        el: cview,
51159        autoScroll:true,
51160        fitToFrame:true,
51161        toolbar: tb,
51162        resizeEl:'cbody'
51163    }
51164 });
51165 </code></pre>
51166      * @param {Object} regions An object containing ContentPanel configs by region name
51167      */
51168     batchAdd : function(regions){
51169         this.beginUpdate();
51170         for(var rname in regions){
51171             var lr = this.regions[rname];
51172             if(lr){
51173                 this.addTypedPanels(lr, regions[rname]);
51174             }
51175         }
51176         this.endUpdate();
51177     },
51178
51179     // private
51180     addTypedPanels : function(lr, ps){
51181         if(typeof ps == 'string'){
51182             lr.add(new Roo.ContentPanel(ps));
51183         }
51184         else if(ps instanceof Array){
51185             for(var i =0, len = ps.length; i < len; i++){
51186                 this.addTypedPanels(lr, ps[i]);
51187             }
51188         }
51189         else if(!ps.events){ // raw config?
51190             var el = ps.el;
51191             delete ps.el; // prevent conflict
51192             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51193         }
51194         else {  // panel object assumed!
51195             lr.add(ps);
51196         }
51197     },
51198     /**
51199      * Adds a xtype elements to the layout.
51200      * <pre><code>
51201
51202 layout.addxtype({
51203        xtype : 'ContentPanel',
51204        region: 'west',
51205        items: [ .... ]
51206    }
51207 );
51208
51209 layout.addxtype({
51210         xtype : 'NestedLayoutPanel',
51211         region: 'west',
51212         layout: {
51213            center: { },
51214            west: { }   
51215         },
51216         items : [ ... list of content panels or nested layout panels.. ]
51217    }
51218 );
51219 </code></pre>
51220      * @param {Object} cfg Xtype definition of item to add.
51221      */
51222     addxtype : function(cfg)
51223     {
51224         // basically accepts a pannel...
51225         // can accept a layout region..!?!?
51226         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51227         
51228         if (!cfg.xtype.match(/Panel$/)) {
51229             return false;
51230         }
51231         var ret = false;
51232         
51233         if (typeof(cfg.region) == 'undefined') {
51234             Roo.log("Failed to add Panel, region was not set");
51235             Roo.log(cfg);
51236             return false;
51237         }
51238         var region = cfg.region;
51239         delete cfg.region;
51240         
51241           
51242         var xitems = [];
51243         if (cfg.items) {
51244             xitems = cfg.items;
51245             delete cfg.items;
51246         }
51247         var nb = false;
51248         
51249         switch(cfg.xtype) 
51250         {
51251             case 'ContentPanel':  // ContentPanel (el, cfg)
51252             case 'ScrollPanel':  // ContentPanel (el, cfg)
51253             case 'ViewPanel': 
51254                 if(cfg.autoCreate) {
51255                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51256                 } else {
51257                     var el = this.el.createChild();
51258                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51259                 }
51260                 
51261                 this.add(region, ret);
51262                 break;
51263             
51264             
51265             case 'TreePanel': // our new panel!
51266                 cfg.el = this.el.createChild();
51267                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51268                 this.add(region, ret);
51269                 break;
51270             
51271             case 'NestedLayoutPanel': 
51272                 // create a new Layout (which is  a Border Layout...
51273                 var el = this.el.createChild();
51274                 var clayout = cfg.layout;
51275                 delete cfg.layout;
51276                 clayout.items   = clayout.items  || [];
51277                 // replace this exitems with the clayout ones..
51278                 xitems = clayout.items;
51279                  
51280                 
51281                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51282                     cfg.background = false;
51283                 }
51284                 var layout = new Roo.BorderLayout(el, clayout);
51285                 
51286                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51287                 //console.log('adding nested layout panel '  + cfg.toSource());
51288                 this.add(region, ret);
51289                 nb = {}; /// find first...
51290                 break;
51291                 
51292             case 'GridPanel': 
51293             
51294                 // needs grid and region
51295                 
51296                 //var el = this.getRegion(region).el.createChild();
51297                 var el = this.el.createChild();
51298                 // create the grid first...
51299                 
51300                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51301                 delete cfg.grid;
51302                 if (region == 'center' && this.active ) {
51303                     cfg.background = false;
51304                 }
51305                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51306                 
51307                 this.add(region, ret);
51308                 if (cfg.background) {
51309                     ret.on('activate', function(gp) {
51310                         if (!gp.grid.rendered) {
51311                             gp.grid.render();
51312                         }
51313                     });
51314                 } else {
51315                     grid.render();
51316                 }
51317                 break;
51318            
51319            
51320            
51321                 
51322                 
51323                 
51324             default:
51325                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51326                     
51327                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51328                     this.add(region, ret);
51329                 } else {
51330                 
51331                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51332                     return null;
51333                 }
51334                 
51335              // GridPanel (grid, cfg)
51336             
51337         }
51338         this.beginUpdate();
51339         // add children..
51340         var region = '';
51341         var abn = {};
51342         Roo.each(xitems, function(i)  {
51343             region = nb && i.region ? i.region : false;
51344             
51345             var add = ret.addxtype(i);
51346            
51347             if (region) {
51348                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51349                 if (!i.background) {
51350                     abn[region] = nb[region] ;
51351                 }
51352             }
51353             
51354         });
51355         this.endUpdate();
51356
51357         // make the last non-background panel active..
51358         //if (nb) { Roo.log(abn); }
51359         if (nb) {
51360             
51361             for(var r in abn) {
51362                 region = this.getRegion(r);
51363                 if (region) {
51364                     // tried using nb[r], but it does not work..
51365                      
51366                     region.showPanel(abn[r]);
51367                    
51368                 }
51369             }
51370         }
51371         return ret;
51372         
51373     }
51374 });
51375
51376 /**
51377  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51378  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51379  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51380  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51381  * <pre><code>
51382 // shorthand
51383 var CP = Roo.ContentPanel;
51384
51385 var layout = Roo.BorderLayout.create({
51386     north: {
51387         initialSize: 25,
51388         titlebar: false,
51389         panels: [new CP("north", "North")]
51390     },
51391     west: {
51392         split:true,
51393         initialSize: 200,
51394         minSize: 175,
51395         maxSize: 400,
51396         titlebar: true,
51397         collapsible: true,
51398         panels: [new CP("west", {title: "West"})]
51399     },
51400     east: {
51401         split:true,
51402         initialSize: 202,
51403         minSize: 175,
51404         maxSize: 400,
51405         titlebar: true,
51406         collapsible: true,
51407         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51408     },
51409     south: {
51410         split:true,
51411         initialSize: 100,
51412         minSize: 100,
51413         maxSize: 200,
51414         titlebar: true,
51415         collapsible: true,
51416         panels: [new CP("south", {title: "South", closable: true})]
51417     },
51418     center: {
51419         titlebar: true,
51420         autoScroll:true,
51421         resizeTabs: true,
51422         minTabWidth: 50,
51423         preferredTabWidth: 150,
51424         panels: [
51425             new CP("center1", {title: "Close Me", closable: true}),
51426             new CP("center2", {title: "Center Panel", closable: false})
51427         ]
51428     }
51429 }, document.body);
51430
51431 layout.getRegion("center").showPanel("center1");
51432 </code></pre>
51433  * @param config
51434  * @param targetEl
51435  */
51436 Roo.BorderLayout.create = function(config, targetEl){
51437     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51438     layout.beginUpdate();
51439     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51440     for(var j = 0, jlen = regions.length; j < jlen; j++){
51441         var lr = regions[j];
51442         if(layout.regions[lr] && config[lr].panels){
51443             var r = layout.regions[lr];
51444             var ps = config[lr].panels;
51445             layout.addTypedPanels(r, ps);
51446         }
51447     }
51448     layout.endUpdate();
51449     return layout;
51450 };
51451
51452 // private
51453 Roo.BorderLayout.RegionFactory = {
51454     // private
51455     validRegions : ["north","south","east","west","center"],
51456
51457     // private
51458     create : function(target, mgr, config){
51459         target = target.toLowerCase();
51460         if(config.lightweight || config.basic){
51461             return new Roo.BasicLayoutRegion(mgr, config, target);
51462         }
51463         switch(target){
51464             case "north":
51465                 return new Roo.NorthLayoutRegion(mgr, config);
51466             case "south":
51467                 return new Roo.SouthLayoutRegion(mgr, config);
51468             case "east":
51469                 return new Roo.EastLayoutRegion(mgr, config);
51470             case "west":
51471                 return new Roo.WestLayoutRegion(mgr, config);
51472             case "center":
51473                 return new Roo.CenterLayoutRegion(mgr, config);
51474         }
51475         throw 'Layout region "'+target+'" not supported.';
51476     }
51477 };/*
51478  * Based on:
51479  * Ext JS Library 1.1.1
51480  * Copyright(c) 2006-2007, Ext JS, LLC.
51481  *
51482  * Originally Released Under LGPL - original licence link has changed is not relivant.
51483  *
51484  * Fork - LGPL
51485  * <script type="text/javascript">
51486  */
51487  
51488 /**
51489  * @class Roo.BasicLayoutRegion
51490  * @extends Roo.util.Observable
51491  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51492  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51493  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51494  */
51495 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51496     this.mgr = mgr;
51497     this.position  = pos;
51498     this.events = {
51499         /**
51500          * @scope Roo.BasicLayoutRegion
51501          */
51502         
51503         /**
51504          * @event beforeremove
51505          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51506          * @param {Roo.LayoutRegion} this
51507          * @param {Roo.ContentPanel} panel The panel
51508          * @param {Object} e The cancel event object
51509          */
51510         "beforeremove" : true,
51511         /**
51512          * @event invalidated
51513          * Fires when the layout for this region is changed.
51514          * @param {Roo.LayoutRegion} this
51515          */
51516         "invalidated" : true,
51517         /**
51518          * @event visibilitychange
51519          * Fires when this region is shown or hidden 
51520          * @param {Roo.LayoutRegion} this
51521          * @param {Boolean} visibility true or false
51522          */
51523         "visibilitychange" : true,
51524         /**
51525          * @event paneladded
51526          * Fires when a panel is added. 
51527          * @param {Roo.LayoutRegion} this
51528          * @param {Roo.ContentPanel} panel The panel
51529          */
51530         "paneladded" : true,
51531         /**
51532          * @event panelremoved
51533          * Fires when a panel is removed. 
51534          * @param {Roo.LayoutRegion} this
51535          * @param {Roo.ContentPanel} panel The panel
51536          */
51537         "panelremoved" : true,
51538         /**
51539          * @event beforecollapse
51540          * Fires when this region before collapse.
51541          * @param {Roo.LayoutRegion} this
51542          */
51543         "beforecollapse" : true,
51544         /**
51545          * @event collapsed
51546          * Fires when this region is collapsed.
51547          * @param {Roo.LayoutRegion} this
51548          */
51549         "collapsed" : true,
51550         /**
51551          * @event expanded
51552          * Fires when this region is expanded.
51553          * @param {Roo.LayoutRegion} this
51554          */
51555         "expanded" : true,
51556         /**
51557          * @event slideshow
51558          * Fires when this region is slid into view.
51559          * @param {Roo.LayoutRegion} this
51560          */
51561         "slideshow" : true,
51562         /**
51563          * @event slidehide
51564          * Fires when this region slides out of view. 
51565          * @param {Roo.LayoutRegion} this
51566          */
51567         "slidehide" : true,
51568         /**
51569          * @event panelactivated
51570          * Fires when a panel is activated. 
51571          * @param {Roo.LayoutRegion} this
51572          * @param {Roo.ContentPanel} panel The activated panel
51573          */
51574         "panelactivated" : true,
51575         /**
51576          * @event resized
51577          * Fires when the user resizes this region. 
51578          * @param {Roo.LayoutRegion} this
51579          * @param {Number} newSize The new size (width for east/west, height for north/south)
51580          */
51581         "resized" : true
51582     };
51583     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51584     this.panels = new Roo.util.MixedCollection();
51585     this.panels.getKey = this.getPanelId.createDelegate(this);
51586     this.box = null;
51587     this.activePanel = null;
51588     // ensure listeners are added...
51589     
51590     if (config.listeners || config.events) {
51591         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51592             listeners : config.listeners || {},
51593             events : config.events || {}
51594         });
51595     }
51596     
51597     if(skipConfig !== true){
51598         this.applyConfig(config);
51599     }
51600 };
51601
51602 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51603     getPanelId : function(p){
51604         return p.getId();
51605     },
51606     
51607     applyConfig : function(config){
51608         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51609         this.config = config;
51610         
51611     },
51612     
51613     /**
51614      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51615      * the width, for horizontal (north, south) the height.
51616      * @param {Number} newSize The new width or height
51617      */
51618     resizeTo : function(newSize){
51619         var el = this.el ? this.el :
51620                  (this.activePanel ? this.activePanel.getEl() : null);
51621         if(el){
51622             switch(this.position){
51623                 case "east":
51624                 case "west":
51625                     el.setWidth(newSize);
51626                     this.fireEvent("resized", this, newSize);
51627                 break;
51628                 case "north":
51629                 case "south":
51630                     el.setHeight(newSize);
51631                     this.fireEvent("resized", this, newSize);
51632                 break;                
51633             }
51634         }
51635     },
51636     
51637     getBox : function(){
51638         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51639     },
51640     
51641     getMargins : function(){
51642         return this.margins;
51643     },
51644     
51645     updateBox : function(box){
51646         this.box = box;
51647         var el = this.activePanel.getEl();
51648         el.dom.style.left = box.x + "px";
51649         el.dom.style.top = box.y + "px";
51650         this.activePanel.setSize(box.width, box.height);
51651     },
51652     
51653     /**
51654      * Returns the container element for this region.
51655      * @return {Roo.Element}
51656      */
51657     getEl : function(){
51658         return this.activePanel;
51659     },
51660     
51661     /**
51662      * Returns true if this region is currently visible.
51663      * @return {Boolean}
51664      */
51665     isVisible : function(){
51666         return this.activePanel ? true : false;
51667     },
51668     
51669     setActivePanel : function(panel){
51670         panel = this.getPanel(panel);
51671         if(this.activePanel && this.activePanel != panel){
51672             this.activePanel.setActiveState(false);
51673             this.activePanel.getEl().setLeftTop(-10000,-10000);
51674         }
51675         this.activePanel = panel;
51676         panel.setActiveState(true);
51677         if(this.box){
51678             panel.setSize(this.box.width, this.box.height);
51679         }
51680         this.fireEvent("panelactivated", this, panel);
51681         this.fireEvent("invalidated");
51682     },
51683     
51684     /**
51685      * Show the specified panel.
51686      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51687      * @return {Roo.ContentPanel} The shown panel or null
51688      */
51689     showPanel : function(panel){
51690         if(panel = this.getPanel(panel)){
51691             this.setActivePanel(panel);
51692         }
51693         return panel;
51694     },
51695     
51696     /**
51697      * Get the active panel for this region.
51698      * @return {Roo.ContentPanel} The active panel or null
51699      */
51700     getActivePanel : function(){
51701         return this.activePanel;
51702     },
51703     
51704     /**
51705      * Add the passed ContentPanel(s)
51706      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51707      * @return {Roo.ContentPanel} The panel added (if only one was added)
51708      */
51709     add : function(panel){
51710         if(arguments.length > 1){
51711             for(var i = 0, len = arguments.length; i < len; i++) {
51712                 this.add(arguments[i]);
51713             }
51714             return null;
51715         }
51716         if(this.hasPanel(panel)){
51717             this.showPanel(panel);
51718             return panel;
51719         }
51720         var el = panel.getEl();
51721         if(el.dom.parentNode != this.mgr.el.dom){
51722             this.mgr.el.dom.appendChild(el.dom);
51723         }
51724         if(panel.setRegion){
51725             panel.setRegion(this);
51726         }
51727         this.panels.add(panel);
51728         el.setStyle("position", "absolute");
51729         if(!panel.background){
51730             this.setActivePanel(panel);
51731             if(this.config.initialSize && this.panels.getCount()==1){
51732                 this.resizeTo(this.config.initialSize);
51733             }
51734         }
51735         this.fireEvent("paneladded", this, panel);
51736         return panel;
51737     },
51738     
51739     /**
51740      * Returns true if the panel is in this region.
51741      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51742      * @return {Boolean}
51743      */
51744     hasPanel : function(panel){
51745         if(typeof panel == "object"){ // must be panel obj
51746             panel = panel.getId();
51747         }
51748         return this.getPanel(panel) ? true : false;
51749     },
51750     
51751     /**
51752      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51753      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51754      * @param {Boolean} preservePanel Overrides the config preservePanel option
51755      * @return {Roo.ContentPanel} The panel that was removed
51756      */
51757     remove : function(panel, preservePanel){
51758         panel = this.getPanel(panel);
51759         if(!panel){
51760             return null;
51761         }
51762         var e = {};
51763         this.fireEvent("beforeremove", this, panel, e);
51764         if(e.cancel === true){
51765             return null;
51766         }
51767         var panelId = panel.getId();
51768         this.panels.removeKey(panelId);
51769         return panel;
51770     },
51771     
51772     /**
51773      * Returns the panel specified or null if it's not in this region.
51774      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51775      * @return {Roo.ContentPanel}
51776      */
51777     getPanel : function(id){
51778         if(typeof id == "object"){ // must be panel obj
51779             return id;
51780         }
51781         return this.panels.get(id);
51782     },
51783     
51784     /**
51785      * Returns this regions position (north/south/east/west/center).
51786      * @return {String} 
51787      */
51788     getPosition: function(){
51789         return this.position;    
51790     }
51791 });/*
51792  * Based on:
51793  * Ext JS Library 1.1.1
51794  * Copyright(c) 2006-2007, Ext JS, LLC.
51795  *
51796  * Originally Released Under LGPL - original licence link has changed is not relivant.
51797  *
51798  * Fork - LGPL
51799  * <script type="text/javascript">
51800  */
51801  
51802 /**
51803  * @class Roo.LayoutRegion
51804  * @extends Roo.BasicLayoutRegion
51805  * This class represents a region in a layout manager.
51806  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51807  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51808  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51809  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51810  * @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})
51811  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51812  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51813  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51814  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51815  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51816  * @cfg {String}    title           The title for the region (overrides panel titles)
51817  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51818  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51819  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51820  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51821  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51822  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51823  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51824  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51825  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51826  * @cfg {Boolean}   showPin         True to show a pin button
51827  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51828  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51829  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51830  * @cfg {Number}    width           For East/West panels
51831  * @cfg {Number}    height          For North/South panels
51832  * @cfg {Boolean}   split           To show the splitter
51833  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51834  */
51835 Roo.LayoutRegion = function(mgr, config, pos){
51836     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51837     var dh = Roo.DomHelper;
51838     /** This region's container element 
51839     * @type Roo.Element */
51840     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51841     /** This region's title element 
51842     * @type Roo.Element */
51843
51844     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51845         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51846         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51847     ]}, true);
51848     this.titleEl.enableDisplayMode();
51849     /** This region's title text element 
51850     * @type HTMLElement */
51851     this.titleTextEl = this.titleEl.dom.firstChild;
51852     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51853     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51854     this.closeBtn.enableDisplayMode();
51855     this.closeBtn.on("click", this.closeClicked, this);
51856     this.closeBtn.hide();
51857
51858     this.createBody(config);
51859     this.visible = true;
51860     this.collapsed = false;
51861
51862     if(config.hideWhenEmpty){
51863         this.hide();
51864         this.on("paneladded", this.validateVisibility, this);
51865         this.on("panelremoved", this.validateVisibility, this);
51866     }
51867     this.applyConfig(config);
51868 };
51869
51870 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51871
51872     createBody : function(){
51873         /** This region's body element 
51874         * @type Roo.Element */
51875         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51876     },
51877
51878     applyConfig : function(c){
51879         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51880             var dh = Roo.DomHelper;
51881             if(c.titlebar !== false){
51882                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51883                 this.collapseBtn.on("click", this.collapse, this);
51884                 this.collapseBtn.enableDisplayMode();
51885
51886                 if(c.showPin === true || this.showPin){
51887                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51888                     this.stickBtn.enableDisplayMode();
51889                     this.stickBtn.on("click", this.expand, this);
51890                     this.stickBtn.hide();
51891                 }
51892             }
51893             /** This region's collapsed element
51894             * @type Roo.Element */
51895             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51896                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51897             ]}, true);
51898             if(c.floatable !== false){
51899                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51900                this.collapsedEl.on("click", this.collapseClick, this);
51901             }
51902
51903             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51904                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51905                    id: "message", unselectable: "on", style:{"float":"left"}});
51906                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51907              }
51908             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51909             this.expandBtn.on("click", this.expand, this);
51910         }
51911         if(this.collapseBtn){
51912             this.collapseBtn.setVisible(c.collapsible == true);
51913         }
51914         this.cmargins = c.cmargins || this.cmargins ||
51915                          (this.position == "west" || this.position == "east" ?
51916                              {top: 0, left: 2, right:2, bottom: 0} :
51917                              {top: 2, left: 0, right:0, bottom: 2});
51918         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51919         this.bottomTabs = c.tabPosition != "top";
51920         this.autoScroll = c.autoScroll || false;
51921         if(this.autoScroll){
51922             this.bodyEl.setStyle("overflow", "auto");
51923         }else{
51924             this.bodyEl.setStyle("overflow", "hidden");
51925         }
51926         //if(c.titlebar !== false){
51927             if((!c.titlebar && !c.title) || c.titlebar === false){
51928                 this.titleEl.hide();
51929             }else{
51930                 this.titleEl.show();
51931                 if(c.title){
51932                     this.titleTextEl.innerHTML = c.title;
51933                 }
51934             }
51935         //}
51936         this.duration = c.duration || .30;
51937         this.slideDuration = c.slideDuration || .45;
51938         this.config = c;
51939         if(c.collapsed){
51940             this.collapse(true);
51941         }
51942         if(c.hidden){
51943             this.hide();
51944         }
51945     },
51946     /**
51947      * Returns true if this region is currently visible.
51948      * @return {Boolean}
51949      */
51950     isVisible : function(){
51951         return this.visible;
51952     },
51953
51954     /**
51955      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51956      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
51957      */
51958     setCollapsedTitle : function(title){
51959         title = title || "&#160;";
51960         if(this.collapsedTitleTextEl){
51961             this.collapsedTitleTextEl.innerHTML = title;
51962         }
51963     },
51964
51965     getBox : function(){
51966         var b;
51967         if(!this.collapsed){
51968             b = this.el.getBox(false, true);
51969         }else{
51970             b = this.collapsedEl.getBox(false, true);
51971         }
51972         return b;
51973     },
51974
51975     getMargins : function(){
51976         return this.collapsed ? this.cmargins : this.margins;
51977     },
51978
51979     highlight : function(){
51980         this.el.addClass("x-layout-panel-dragover");
51981     },
51982
51983     unhighlight : function(){
51984         this.el.removeClass("x-layout-panel-dragover");
51985     },
51986
51987     updateBox : function(box){
51988         this.box = box;
51989         if(!this.collapsed){
51990             this.el.dom.style.left = box.x + "px";
51991             this.el.dom.style.top = box.y + "px";
51992             this.updateBody(box.width, box.height);
51993         }else{
51994             this.collapsedEl.dom.style.left = box.x + "px";
51995             this.collapsedEl.dom.style.top = box.y + "px";
51996             this.collapsedEl.setSize(box.width, box.height);
51997         }
51998         if(this.tabs){
51999             this.tabs.autoSizeTabs();
52000         }
52001     },
52002
52003     updateBody : function(w, h){
52004         if(w !== null){
52005             this.el.setWidth(w);
52006             w -= this.el.getBorderWidth("rl");
52007             if(this.config.adjustments){
52008                 w += this.config.adjustments[0];
52009             }
52010         }
52011         if(h !== null){
52012             this.el.setHeight(h);
52013             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52014             h -= this.el.getBorderWidth("tb");
52015             if(this.config.adjustments){
52016                 h += this.config.adjustments[1];
52017             }
52018             this.bodyEl.setHeight(h);
52019             if(this.tabs){
52020                 h = this.tabs.syncHeight(h);
52021             }
52022         }
52023         if(this.panelSize){
52024             w = w !== null ? w : this.panelSize.width;
52025             h = h !== null ? h : this.panelSize.height;
52026         }
52027         if(this.activePanel){
52028             var el = this.activePanel.getEl();
52029             w = w !== null ? w : el.getWidth();
52030             h = h !== null ? h : el.getHeight();
52031             this.panelSize = {width: w, height: h};
52032             this.activePanel.setSize(w, h);
52033         }
52034         if(Roo.isIE && this.tabs){
52035             this.tabs.el.repaint();
52036         }
52037     },
52038
52039     /**
52040      * Returns the container element for this region.
52041      * @return {Roo.Element}
52042      */
52043     getEl : function(){
52044         return this.el;
52045     },
52046
52047     /**
52048      * Hides this region.
52049      */
52050     hide : function(){
52051         if(!this.collapsed){
52052             this.el.dom.style.left = "-2000px";
52053             this.el.hide();
52054         }else{
52055             this.collapsedEl.dom.style.left = "-2000px";
52056             this.collapsedEl.hide();
52057         }
52058         this.visible = false;
52059         this.fireEvent("visibilitychange", this, false);
52060     },
52061
52062     /**
52063      * Shows this region if it was previously hidden.
52064      */
52065     show : function(){
52066         if(!this.collapsed){
52067             this.el.show();
52068         }else{
52069             this.collapsedEl.show();
52070         }
52071         this.visible = true;
52072         this.fireEvent("visibilitychange", this, true);
52073     },
52074
52075     closeClicked : function(){
52076         if(this.activePanel){
52077             this.remove(this.activePanel);
52078         }
52079     },
52080
52081     collapseClick : function(e){
52082         if(this.isSlid){
52083            e.stopPropagation();
52084            this.slideIn();
52085         }else{
52086            e.stopPropagation();
52087            this.slideOut();
52088         }
52089     },
52090
52091     /**
52092      * Collapses this region.
52093      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52094      */
52095     collapse : function(skipAnim, skipCheck = false){
52096         if(this.collapsed) {
52097             return;
52098         }
52099         
52100         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52101             
52102             this.collapsed = true;
52103             if(this.split){
52104                 this.split.el.hide();
52105             }
52106             if(this.config.animate && skipAnim !== true){
52107                 this.fireEvent("invalidated", this);
52108                 this.animateCollapse();
52109             }else{
52110                 this.el.setLocation(-20000,-20000);
52111                 this.el.hide();
52112                 this.collapsedEl.show();
52113                 this.fireEvent("collapsed", this);
52114                 this.fireEvent("invalidated", this);
52115             }
52116         }
52117         
52118     },
52119
52120     animateCollapse : function(){
52121         // overridden
52122     },
52123
52124     /**
52125      * Expands this region if it was previously collapsed.
52126      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52127      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52128      */
52129     expand : function(e, skipAnim){
52130         if(e) {
52131             e.stopPropagation();
52132         }
52133         if(!this.collapsed || this.el.hasActiveFx()) {
52134             return;
52135         }
52136         if(this.isSlid){
52137             this.afterSlideIn();
52138             skipAnim = true;
52139         }
52140         this.collapsed = false;
52141         if(this.config.animate && skipAnim !== true){
52142             this.animateExpand();
52143         }else{
52144             this.el.show();
52145             if(this.split){
52146                 this.split.el.show();
52147             }
52148             this.collapsedEl.setLocation(-2000,-2000);
52149             this.collapsedEl.hide();
52150             this.fireEvent("invalidated", this);
52151             this.fireEvent("expanded", this);
52152         }
52153     },
52154
52155     animateExpand : function(){
52156         // overridden
52157     },
52158
52159     initTabs : function()
52160     {
52161         this.bodyEl.setStyle("overflow", "hidden");
52162         var ts = new Roo.TabPanel(
52163                 this.bodyEl.dom,
52164                 {
52165                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52166                     disableTooltips: this.config.disableTabTips,
52167                     toolbar : this.config.toolbar
52168                 }
52169         );
52170         if(this.config.hideTabs){
52171             ts.stripWrap.setDisplayed(false);
52172         }
52173         this.tabs = ts;
52174         ts.resizeTabs = this.config.resizeTabs === true;
52175         ts.minTabWidth = this.config.minTabWidth || 40;
52176         ts.maxTabWidth = this.config.maxTabWidth || 250;
52177         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52178         ts.monitorResize = false;
52179         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52180         ts.bodyEl.addClass('x-layout-tabs-body');
52181         this.panels.each(this.initPanelAsTab, this);
52182     },
52183
52184     initPanelAsTab : function(panel){
52185         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52186                     this.config.closeOnTab && panel.isClosable());
52187         if(panel.tabTip !== undefined){
52188             ti.setTooltip(panel.tabTip);
52189         }
52190         ti.on("activate", function(){
52191               this.setActivePanel(panel);
52192         }, this);
52193         if(this.config.closeOnTab){
52194             ti.on("beforeclose", function(t, e){
52195                 e.cancel = true;
52196                 this.remove(panel);
52197             }, this);
52198         }
52199         return ti;
52200     },
52201
52202     updatePanelTitle : function(panel, title){
52203         if(this.activePanel == panel){
52204             this.updateTitle(title);
52205         }
52206         if(this.tabs){
52207             var ti = this.tabs.getTab(panel.getEl().id);
52208             ti.setText(title);
52209             if(panel.tabTip !== undefined){
52210                 ti.setTooltip(panel.tabTip);
52211             }
52212         }
52213     },
52214
52215     updateTitle : function(title){
52216         if(this.titleTextEl && !this.config.title){
52217             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52218         }
52219     },
52220
52221     setActivePanel : function(panel){
52222         panel = this.getPanel(panel);
52223         if(this.activePanel && this.activePanel != panel){
52224             this.activePanel.setActiveState(false);
52225         }
52226         this.activePanel = panel;
52227         panel.setActiveState(true);
52228         if(this.panelSize){
52229             panel.setSize(this.panelSize.width, this.panelSize.height);
52230         }
52231         if(this.closeBtn){
52232             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52233         }
52234         this.updateTitle(panel.getTitle());
52235         if(this.tabs){
52236             this.fireEvent("invalidated", this);
52237         }
52238         this.fireEvent("panelactivated", this, panel);
52239     },
52240
52241     /**
52242      * Shows the specified panel.
52243      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52244      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52245      */
52246     showPanel : function(panel)
52247     {
52248         panel = this.getPanel(panel);
52249         if(panel){
52250             if(this.tabs){
52251                 var tab = this.tabs.getTab(panel.getEl().id);
52252                 if(tab.isHidden()){
52253                     this.tabs.unhideTab(tab.id);
52254                 }
52255                 tab.activate();
52256             }else{
52257                 this.setActivePanel(panel);
52258             }
52259         }
52260         return panel;
52261     },
52262
52263     /**
52264      * Get the active panel for this region.
52265      * @return {Roo.ContentPanel} The active panel or null
52266      */
52267     getActivePanel : function(){
52268         return this.activePanel;
52269     },
52270
52271     validateVisibility : function(){
52272         if(this.panels.getCount() < 1){
52273             this.updateTitle("&#160;");
52274             this.closeBtn.hide();
52275             this.hide();
52276         }else{
52277             if(!this.isVisible()){
52278                 this.show();
52279             }
52280         }
52281     },
52282
52283     /**
52284      * Adds the passed ContentPanel(s) to this region.
52285      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52286      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52287      */
52288     add : function(panel){
52289         if(arguments.length > 1){
52290             for(var i = 0, len = arguments.length; i < len; i++) {
52291                 this.add(arguments[i]);
52292             }
52293             return null;
52294         }
52295         if(this.hasPanel(panel)){
52296             this.showPanel(panel);
52297             return panel;
52298         }
52299         panel.setRegion(this);
52300         this.panels.add(panel);
52301         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52302             this.bodyEl.dom.appendChild(panel.getEl().dom);
52303             if(panel.background !== true){
52304                 this.setActivePanel(panel);
52305             }
52306             this.fireEvent("paneladded", this, panel);
52307             return panel;
52308         }
52309         if(!this.tabs){
52310             this.initTabs();
52311         }else{
52312             this.initPanelAsTab(panel);
52313         }
52314         if(panel.background !== true){
52315             this.tabs.activate(panel.getEl().id);
52316         }
52317         this.fireEvent("paneladded", this, panel);
52318         return panel;
52319     },
52320
52321     /**
52322      * Hides the tab for the specified panel.
52323      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52324      */
52325     hidePanel : function(panel){
52326         if(this.tabs && (panel = this.getPanel(panel))){
52327             this.tabs.hideTab(panel.getEl().id);
52328         }
52329     },
52330
52331     /**
52332      * Unhides the tab for a previously hidden panel.
52333      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52334      */
52335     unhidePanel : function(panel){
52336         if(this.tabs && (panel = this.getPanel(panel))){
52337             this.tabs.unhideTab(panel.getEl().id);
52338         }
52339     },
52340
52341     clearPanels : function(){
52342         while(this.panels.getCount() > 0){
52343              this.remove(this.panels.first());
52344         }
52345     },
52346
52347     /**
52348      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52349      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52350      * @param {Boolean} preservePanel Overrides the config preservePanel option
52351      * @return {Roo.ContentPanel} The panel that was removed
52352      */
52353     remove : function(panel, preservePanel){
52354         panel = this.getPanel(panel);
52355         if(!panel){
52356             return null;
52357         }
52358         var e = {};
52359         this.fireEvent("beforeremove", this, panel, e);
52360         if(e.cancel === true){
52361             return null;
52362         }
52363         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52364         var panelId = panel.getId();
52365         this.panels.removeKey(panelId);
52366         if(preservePanel){
52367             document.body.appendChild(panel.getEl().dom);
52368         }
52369         if(this.tabs){
52370             this.tabs.removeTab(panel.getEl().id);
52371         }else if (!preservePanel){
52372             this.bodyEl.dom.removeChild(panel.getEl().dom);
52373         }
52374         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52375             var p = this.panels.first();
52376             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52377             tempEl.appendChild(p.getEl().dom);
52378             this.bodyEl.update("");
52379             this.bodyEl.dom.appendChild(p.getEl().dom);
52380             tempEl = null;
52381             this.updateTitle(p.getTitle());
52382             this.tabs = null;
52383             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52384             this.setActivePanel(p);
52385         }
52386         panel.setRegion(null);
52387         if(this.activePanel == panel){
52388             this.activePanel = null;
52389         }
52390         if(this.config.autoDestroy !== false && preservePanel !== true){
52391             try{panel.destroy();}catch(e){}
52392         }
52393         this.fireEvent("panelremoved", this, panel);
52394         return panel;
52395     },
52396
52397     /**
52398      * Returns the TabPanel component used by this region
52399      * @return {Roo.TabPanel}
52400      */
52401     getTabs : function(){
52402         return this.tabs;
52403     },
52404
52405     createTool : function(parentEl, className){
52406         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52407             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52408         btn.addClassOnOver("x-layout-tools-button-over");
52409         return btn;
52410     }
52411 });/*
52412  * Based on:
52413  * Ext JS Library 1.1.1
52414  * Copyright(c) 2006-2007, Ext JS, LLC.
52415  *
52416  * Originally Released Under LGPL - original licence link has changed is not relivant.
52417  *
52418  * Fork - LGPL
52419  * <script type="text/javascript">
52420  */
52421  
52422
52423
52424 /**
52425  * @class Roo.SplitLayoutRegion
52426  * @extends Roo.LayoutRegion
52427  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52428  */
52429 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52430     this.cursor = cursor;
52431     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52432 };
52433
52434 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52435     splitTip : "Drag to resize.",
52436     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52437     useSplitTips : false,
52438
52439     applyConfig : function(config){
52440         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52441         if(config.split){
52442             if(!this.split){
52443                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52444                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52445                 /** The SplitBar for this region 
52446                 * @type Roo.SplitBar */
52447                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52448                 this.split.on("moved", this.onSplitMove, this);
52449                 this.split.useShim = config.useShim === true;
52450                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52451                 if(this.useSplitTips){
52452                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52453                 }
52454                 if(config.collapsible){
52455                     this.split.el.on("dblclick", this.collapse,  this);
52456                 }
52457             }
52458             if(typeof config.minSize != "undefined"){
52459                 this.split.minSize = config.minSize;
52460             }
52461             if(typeof config.maxSize != "undefined"){
52462                 this.split.maxSize = config.maxSize;
52463             }
52464             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52465                 this.hideSplitter();
52466             }
52467         }
52468     },
52469
52470     getHMaxSize : function(){
52471          var cmax = this.config.maxSize || 10000;
52472          var center = this.mgr.getRegion("center");
52473          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52474     },
52475
52476     getVMaxSize : function(){
52477          var cmax = this.config.maxSize || 10000;
52478          var center = this.mgr.getRegion("center");
52479          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52480     },
52481
52482     onSplitMove : function(split, newSize){
52483         this.fireEvent("resized", this, newSize);
52484     },
52485     
52486     /** 
52487      * Returns the {@link Roo.SplitBar} for this region.
52488      * @return {Roo.SplitBar}
52489      */
52490     getSplitBar : function(){
52491         return this.split;
52492     },
52493     
52494     hide : function(){
52495         this.hideSplitter();
52496         Roo.SplitLayoutRegion.superclass.hide.call(this);
52497     },
52498
52499     hideSplitter : function(){
52500         if(this.split){
52501             this.split.el.setLocation(-2000,-2000);
52502             this.split.el.hide();
52503         }
52504     },
52505
52506     show : function(){
52507         if(this.split){
52508             this.split.el.show();
52509         }
52510         Roo.SplitLayoutRegion.superclass.show.call(this);
52511     },
52512     
52513     beforeSlide: function(){
52514         if(Roo.isGecko){// firefox overflow auto bug workaround
52515             this.bodyEl.clip();
52516             if(this.tabs) {
52517                 this.tabs.bodyEl.clip();
52518             }
52519             if(this.activePanel){
52520                 this.activePanel.getEl().clip();
52521                 
52522                 if(this.activePanel.beforeSlide){
52523                     this.activePanel.beforeSlide();
52524                 }
52525             }
52526         }
52527     },
52528     
52529     afterSlide : function(){
52530         if(Roo.isGecko){// firefox overflow auto bug workaround
52531             this.bodyEl.unclip();
52532             if(this.tabs) {
52533                 this.tabs.bodyEl.unclip();
52534             }
52535             if(this.activePanel){
52536                 this.activePanel.getEl().unclip();
52537                 if(this.activePanel.afterSlide){
52538                     this.activePanel.afterSlide();
52539                 }
52540             }
52541         }
52542     },
52543
52544     initAutoHide : function(){
52545         if(this.autoHide !== false){
52546             if(!this.autoHideHd){
52547                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52548                 this.autoHideHd = {
52549                     "mouseout": function(e){
52550                         if(!e.within(this.el, true)){
52551                             st.delay(500);
52552                         }
52553                     },
52554                     "mouseover" : function(e){
52555                         st.cancel();
52556                     },
52557                     scope : this
52558                 };
52559             }
52560             this.el.on(this.autoHideHd);
52561         }
52562     },
52563
52564     clearAutoHide : function(){
52565         if(this.autoHide !== false){
52566             this.el.un("mouseout", this.autoHideHd.mouseout);
52567             this.el.un("mouseover", this.autoHideHd.mouseover);
52568         }
52569     },
52570
52571     clearMonitor : function(){
52572         Roo.get(document).un("click", this.slideInIf, this);
52573     },
52574
52575     // these names are backwards but not changed for compat
52576     slideOut : function(){
52577         if(this.isSlid || this.el.hasActiveFx()){
52578             return;
52579         }
52580         this.isSlid = true;
52581         if(this.collapseBtn){
52582             this.collapseBtn.hide();
52583         }
52584         this.closeBtnState = this.closeBtn.getStyle('display');
52585         this.closeBtn.hide();
52586         if(this.stickBtn){
52587             this.stickBtn.show();
52588         }
52589         this.el.show();
52590         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52591         this.beforeSlide();
52592         this.el.setStyle("z-index", 10001);
52593         this.el.slideIn(this.getSlideAnchor(), {
52594             callback: function(){
52595                 this.afterSlide();
52596                 this.initAutoHide();
52597                 Roo.get(document).on("click", this.slideInIf, this);
52598                 this.fireEvent("slideshow", this);
52599             },
52600             scope: this,
52601             block: true
52602         });
52603     },
52604
52605     afterSlideIn : function(){
52606         this.clearAutoHide();
52607         this.isSlid = false;
52608         this.clearMonitor();
52609         this.el.setStyle("z-index", "");
52610         if(this.collapseBtn){
52611             this.collapseBtn.show();
52612         }
52613         this.closeBtn.setStyle('display', this.closeBtnState);
52614         if(this.stickBtn){
52615             this.stickBtn.hide();
52616         }
52617         this.fireEvent("slidehide", this);
52618     },
52619
52620     slideIn : function(cb){
52621         if(!this.isSlid || this.el.hasActiveFx()){
52622             Roo.callback(cb);
52623             return;
52624         }
52625         this.isSlid = false;
52626         this.beforeSlide();
52627         this.el.slideOut(this.getSlideAnchor(), {
52628             callback: function(){
52629                 this.el.setLeftTop(-10000, -10000);
52630                 this.afterSlide();
52631                 this.afterSlideIn();
52632                 Roo.callback(cb);
52633             },
52634             scope: this,
52635             block: true
52636         });
52637     },
52638     
52639     slideInIf : function(e){
52640         if(!e.within(this.el)){
52641             this.slideIn();
52642         }
52643     },
52644
52645     animateCollapse : function(){
52646         this.beforeSlide();
52647         this.el.setStyle("z-index", 20000);
52648         var anchor = this.getSlideAnchor();
52649         this.el.slideOut(anchor, {
52650             callback : function(){
52651                 this.el.setStyle("z-index", "");
52652                 this.collapsedEl.slideIn(anchor, {duration:.3});
52653                 this.afterSlide();
52654                 this.el.setLocation(-10000,-10000);
52655                 this.el.hide();
52656                 this.fireEvent("collapsed", this);
52657             },
52658             scope: this,
52659             block: true
52660         });
52661     },
52662
52663     animateExpand : function(){
52664         this.beforeSlide();
52665         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52666         this.el.setStyle("z-index", 20000);
52667         this.collapsedEl.hide({
52668             duration:.1
52669         });
52670         this.el.slideIn(this.getSlideAnchor(), {
52671             callback : function(){
52672                 this.el.setStyle("z-index", "");
52673                 this.afterSlide();
52674                 if(this.split){
52675                     this.split.el.show();
52676                 }
52677                 this.fireEvent("invalidated", this);
52678                 this.fireEvent("expanded", this);
52679             },
52680             scope: this,
52681             block: true
52682         });
52683     },
52684
52685     anchors : {
52686         "west" : "left",
52687         "east" : "right",
52688         "north" : "top",
52689         "south" : "bottom"
52690     },
52691
52692     sanchors : {
52693         "west" : "l",
52694         "east" : "r",
52695         "north" : "t",
52696         "south" : "b"
52697     },
52698
52699     canchors : {
52700         "west" : "tl-tr",
52701         "east" : "tr-tl",
52702         "north" : "tl-bl",
52703         "south" : "bl-tl"
52704     },
52705
52706     getAnchor : function(){
52707         return this.anchors[this.position];
52708     },
52709
52710     getCollapseAnchor : function(){
52711         return this.canchors[this.position];
52712     },
52713
52714     getSlideAnchor : function(){
52715         return this.sanchors[this.position];
52716     },
52717
52718     getAlignAdj : function(){
52719         var cm = this.cmargins;
52720         switch(this.position){
52721             case "west":
52722                 return [0, 0];
52723             break;
52724             case "east":
52725                 return [0, 0];
52726             break;
52727             case "north":
52728                 return [0, 0];
52729             break;
52730             case "south":
52731                 return [0, 0];
52732             break;
52733         }
52734     },
52735
52736     getExpandAdj : function(){
52737         var c = this.collapsedEl, cm = this.cmargins;
52738         switch(this.position){
52739             case "west":
52740                 return [-(cm.right+c.getWidth()+cm.left), 0];
52741             break;
52742             case "east":
52743                 return [cm.right+c.getWidth()+cm.left, 0];
52744             break;
52745             case "north":
52746                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52747             break;
52748             case "south":
52749                 return [0, cm.top+cm.bottom+c.getHeight()];
52750             break;
52751         }
52752     }
52753 });/*
52754  * Based on:
52755  * Ext JS Library 1.1.1
52756  * Copyright(c) 2006-2007, Ext JS, LLC.
52757  *
52758  * Originally Released Under LGPL - original licence link has changed is not relivant.
52759  *
52760  * Fork - LGPL
52761  * <script type="text/javascript">
52762  */
52763 /*
52764  * These classes are private internal classes
52765  */
52766 Roo.CenterLayoutRegion = function(mgr, config){
52767     Roo.LayoutRegion.call(this, mgr, config, "center");
52768     this.visible = true;
52769     this.minWidth = config.minWidth || 20;
52770     this.minHeight = config.minHeight || 20;
52771 };
52772
52773 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52774     hide : function(){
52775         // center panel can't be hidden
52776     },
52777     
52778     show : function(){
52779         // center panel can't be hidden
52780     },
52781     
52782     getMinWidth: function(){
52783         return this.minWidth;
52784     },
52785     
52786     getMinHeight: function(){
52787         return this.minHeight;
52788     }
52789 });
52790
52791
52792 Roo.NorthLayoutRegion = function(mgr, config){
52793     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52794     if(this.split){
52795         this.split.placement = Roo.SplitBar.TOP;
52796         this.split.orientation = Roo.SplitBar.VERTICAL;
52797         this.split.el.addClass("x-layout-split-v");
52798     }
52799     var size = config.initialSize || config.height;
52800     if(typeof size != "undefined"){
52801         this.el.setHeight(size);
52802     }
52803 };
52804 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52805     orientation: Roo.SplitBar.VERTICAL,
52806     getBox : function(){
52807         if(this.collapsed){
52808             return this.collapsedEl.getBox();
52809         }
52810         var box = this.el.getBox();
52811         if(this.split){
52812             box.height += this.split.el.getHeight();
52813         }
52814         return box;
52815     },
52816     
52817     updateBox : function(box){
52818         if(this.split && !this.collapsed){
52819             box.height -= this.split.el.getHeight();
52820             this.split.el.setLeft(box.x);
52821             this.split.el.setTop(box.y+box.height);
52822             this.split.el.setWidth(box.width);
52823         }
52824         if(this.collapsed){
52825             this.updateBody(box.width, null);
52826         }
52827         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52828     }
52829 });
52830
52831 Roo.SouthLayoutRegion = function(mgr, config){
52832     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52833     if(this.split){
52834         this.split.placement = Roo.SplitBar.BOTTOM;
52835         this.split.orientation = Roo.SplitBar.VERTICAL;
52836         this.split.el.addClass("x-layout-split-v");
52837     }
52838     var size = config.initialSize || config.height;
52839     if(typeof size != "undefined"){
52840         this.el.setHeight(size);
52841     }
52842 };
52843 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52844     orientation: Roo.SplitBar.VERTICAL,
52845     getBox : function(){
52846         if(this.collapsed){
52847             return this.collapsedEl.getBox();
52848         }
52849         var box = this.el.getBox();
52850         if(this.split){
52851             var sh = this.split.el.getHeight();
52852             box.height += sh;
52853             box.y -= sh;
52854         }
52855         return box;
52856     },
52857     
52858     updateBox : function(box){
52859         if(this.split && !this.collapsed){
52860             var sh = this.split.el.getHeight();
52861             box.height -= sh;
52862             box.y += sh;
52863             this.split.el.setLeft(box.x);
52864             this.split.el.setTop(box.y-sh);
52865             this.split.el.setWidth(box.width);
52866         }
52867         if(this.collapsed){
52868             this.updateBody(box.width, null);
52869         }
52870         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52871     }
52872 });
52873
52874 Roo.EastLayoutRegion = function(mgr, config){
52875     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52876     if(this.split){
52877         this.split.placement = Roo.SplitBar.RIGHT;
52878         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52879         this.split.el.addClass("x-layout-split-h");
52880     }
52881     var size = config.initialSize || config.width;
52882     if(typeof size != "undefined"){
52883         this.el.setWidth(size);
52884     }
52885 };
52886 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52887     orientation: Roo.SplitBar.HORIZONTAL,
52888     getBox : function(){
52889         if(this.collapsed){
52890             return this.collapsedEl.getBox();
52891         }
52892         var box = this.el.getBox();
52893         if(this.split){
52894             var sw = this.split.el.getWidth();
52895             box.width += sw;
52896             box.x -= sw;
52897         }
52898         return box;
52899     },
52900
52901     updateBox : function(box){
52902         if(this.split && !this.collapsed){
52903             var sw = this.split.el.getWidth();
52904             box.width -= sw;
52905             this.split.el.setLeft(box.x);
52906             this.split.el.setTop(box.y);
52907             this.split.el.setHeight(box.height);
52908             box.x += sw;
52909         }
52910         if(this.collapsed){
52911             this.updateBody(null, box.height);
52912         }
52913         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52914     }
52915 });
52916
52917 Roo.WestLayoutRegion = function(mgr, config){
52918     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52919     if(this.split){
52920         this.split.placement = Roo.SplitBar.LEFT;
52921         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52922         this.split.el.addClass("x-layout-split-h");
52923     }
52924     var size = config.initialSize || config.width;
52925     if(typeof size != "undefined"){
52926         this.el.setWidth(size);
52927     }
52928 };
52929 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52930     orientation: Roo.SplitBar.HORIZONTAL,
52931     getBox : function(){
52932         if(this.collapsed){
52933             return this.collapsedEl.getBox();
52934         }
52935         var box = this.el.getBox();
52936         if(this.split){
52937             box.width += this.split.el.getWidth();
52938         }
52939         return box;
52940     },
52941     
52942     updateBox : function(box){
52943         if(this.split && !this.collapsed){
52944             var sw = this.split.el.getWidth();
52945             box.width -= sw;
52946             this.split.el.setLeft(box.x+box.width);
52947             this.split.el.setTop(box.y);
52948             this.split.el.setHeight(box.height);
52949         }
52950         if(this.collapsed){
52951             this.updateBody(null, box.height);
52952         }
52953         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52954     }
52955 });
52956 /*
52957  * Based on:
52958  * Ext JS Library 1.1.1
52959  * Copyright(c) 2006-2007, Ext JS, LLC.
52960  *
52961  * Originally Released Under LGPL - original licence link has changed is not relivant.
52962  *
52963  * Fork - LGPL
52964  * <script type="text/javascript">
52965  */
52966  
52967  
52968 /*
52969  * Private internal class for reading and applying state
52970  */
52971 Roo.LayoutStateManager = function(layout){
52972      // default empty state
52973      this.state = {
52974         north: {},
52975         south: {},
52976         east: {},
52977         west: {}       
52978     };
52979 };
52980
52981 Roo.LayoutStateManager.prototype = {
52982     init : function(layout, provider){
52983         this.provider = provider;
52984         var state = provider.get(layout.id+"-layout-state");
52985         if(state){
52986             var wasUpdating = layout.isUpdating();
52987             if(!wasUpdating){
52988                 layout.beginUpdate();
52989             }
52990             for(var key in state){
52991                 if(typeof state[key] != "function"){
52992                     var rstate = state[key];
52993                     var r = layout.getRegion(key);
52994                     if(r && rstate){
52995                         if(rstate.size){
52996                             r.resizeTo(rstate.size);
52997                         }
52998                         if(rstate.collapsed == true){
52999                             r.collapse(true);
53000                         }else{
53001                             r.expand(null, true);
53002                         }
53003                     }
53004                 }
53005             }
53006             if(!wasUpdating){
53007                 layout.endUpdate();
53008             }
53009             this.state = state; 
53010         }
53011         this.layout = layout;
53012         layout.on("regionresized", this.onRegionResized, this);
53013         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53014         layout.on("regionexpanded", this.onRegionExpanded, this);
53015     },
53016     
53017     storeState : function(){
53018         this.provider.set(this.layout.id+"-layout-state", this.state);
53019     },
53020     
53021     onRegionResized : function(region, newSize){
53022         this.state[region.getPosition()].size = newSize;
53023         this.storeState();
53024     },
53025     
53026     onRegionCollapsed : function(region){
53027         this.state[region.getPosition()].collapsed = true;
53028         this.storeState();
53029     },
53030     
53031     onRegionExpanded : function(region){
53032         this.state[region.getPosition()].collapsed = false;
53033         this.storeState();
53034     }
53035 };/*
53036  * Based on:
53037  * Ext JS Library 1.1.1
53038  * Copyright(c) 2006-2007, Ext JS, LLC.
53039  *
53040  * Originally Released Under LGPL - original licence link has changed is not relivant.
53041  *
53042  * Fork - LGPL
53043  * <script type="text/javascript">
53044  */
53045 /**
53046  * @class Roo.ContentPanel
53047  * @extends Roo.util.Observable
53048  * A basic ContentPanel element.
53049  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53050  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53051  * @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
53052  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53053  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53054  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53055  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53056  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53057  * @cfg {String} title          The title for this panel
53058  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53059  * @cfg {String} url            Calls {@link #setUrl} with this value
53060  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53061  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53062  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53063  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53064
53065  * @constructor
53066  * Create a new ContentPanel.
53067  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53068  * @param {String/Object} config A string to set only the title or a config object
53069  * @param {String} content (optional) Set the HTML content for this panel
53070  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53071  */
53072 Roo.ContentPanel = function(el, config, content){
53073     
53074      
53075     /*
53076     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53077         config = el;
53078         el = Roo.id();
53079     }
53080     if (config && config.parentLayout) { 
53081         el = config.parentLayout.el.createChild(); 
53082     }
53083     */
53084     if(el.autoCreate){ // xtype is available if this is called from factory
53085         config = el;
53086         el = Roo.id();
53087     }
53088     this.el = Roo.get(el);
53089     if(!this.el && config && config.autoCreate){
53090         if(typeof config.autoCreate == "object"){
53091             if(!config.autoCreate.id){
53092                 config.autoCreate.id = config.id||el;
53093             }
53094             this.el = Roo.DomHelper.append(document.body,
53095                         config.autoCreate, true);
53096         }else{
53097             this.el = Roo.DomHelper.append(document.body,
53098                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53099         }
53100     }
53101     this.closable = false;
53102     this.loaded = false;
53103     this.active = false;
53104     if(typeof config == "string"){
53105         this.title = config;
53106     }else{
53107         Roo.apply(this, config);
53108     }
53109     
53110     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53111         this.wrapEl = this.el.wrap();
53112         this.toolbar.container = this.el.insertSibling(false, 'before');
53113         this.toolbar = new Roo.Toolbar(this.toolbar);
53114     }
53115     
53116     // xtype created footer. - not sure if will work as we normally have to render first..
53117     if (this.footer && !this.footer.el && this.footer.xtype) {
53118         if (!this.wrapEl) {
53119             this.wrapEl = this.el.wrap();
53120         }
53121     
53122         this.footer.container = this.wrapEl.createChild();
53123          
53124         this.footer = Roo.factory(this.footer, Roo);
53125         
53126     }
53127     
53128     if(this.resizeEl){
53129         this.resizeEl = Roo.get(this.resizeEl, true);
53130     }else{
53131         this.resizeEl = this.el;
53132     }
53133     // handle view.xtype
53134     
53135  
53136     
53137     
53138     this.addEvents({
53139         /**
53140          * @event activate
53141          * Fires when this panel is activated. 
53142          * @param {Roo.ContentPanel} this
53143          */
53144         "activate" : true,
53145         /**
53146          * @event deactivate
53147          * Fires when this panel is activated. 
53148          * @param {Roo.ContentPanel} this
53149          */
53150         "deactivate" : true,
53151
53152         /**
53153          * @event resize
53154          * Fires when this panel is resized if fitToFrame is true.
53155          * @param {Roo.ContentPanel} this
53156          * @param {Number} width The width after any component adjustments
53157          * @param {Number} height The height after any component adjustments
53158          */
53159         "resize" : true,
53160         
53161          /**
53162          * @event render
53163          * Fires when this tab is created
53164          * @param {Roo.ContentPanel} this
53165          */
53166         "render" : true
53167         
53168         
53169         
53170     });
53171     
53172
53173     
53174     
53175     if(this.autoScroll){
53176         this.resizeEl.setStyle("overflow", "auto");
53177     } else {
53178         // fix randome scrolling
53179         this.el.on('scroll', function() {
53180             Roo.log('fix random scolling');
53181             this.scrollTo('top',0); 
53182         });
53183     }
53184     content = content || this.content;
53185     if(content){
53186         this.setContent(content);
53187     }
53188     if(config && config.url){
53189         this.setUrl(this.url, this.params, this.loadOnce);
53190     }
53191     
53192     
53193     
53194     Roo.ContentPanel.superclass.constructor.call(this);
53195     
53196     if (this.view && typeof(this.view.xtype) != 'undefined') {
53197         this.view.el = this.el.appendChild(document.createElement("div"));
53198         this.view = Roo.factory(this.view); 
53199         this.view.render  &&  this.view.render(false, '');  
53200     }
53201     
53202     
53203     this.fireEvent('render', this);
53204 };
53205
53206 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53207     tabTip:'',
53208     setRegion : function(region){
53209         this.region = region;
53210         if(region){
53211            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53212         }else{
53213            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53214         } 
53215     },
53216     
53217     /**
53218      * Returns the toolbar for this Panel if one was configured. 
53219      * @return {Roo.Toolbar} 
53220      */
53221     getToolbar : function(){
53222         return this.toolbar;
53223     },
53224     
53225     setActiveState : function(active){
53226         this.active = active;
53227         if(!active){
53228             this.fireEvent("deactivate", this);
53229         }else{
53230             this.fireEvent("activate", this);
53231         }
53232     },
53233     /**
53234      * Updates this panel's element
53235      * @param {String} content The new content
53236      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53237     */
53238     setContent : function(content, loadScripts){
53239         this.el.update(content, loadScripts);
53240     },
53241
53242     ignoreResize : function(w, h){
53243         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53244             return true;
53245         }else{
53246             this.lastSize = {width: w, height: h};
53247             return false;
53248         }
53249     },
53250     /**
53251      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53252      * @return {Roo.UpdateManager} The UpdateManager
53253      */
53254     getUpdateManager : function(){
53255         return this.el.getUpdateManager();
53256     },
53257      /**
53258      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53259      * @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:
53260 <pre><code>
53261 panel.load({
53262     url: "your-url.php",
53263     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53264     callback: yourFunction,
53265     scope: yourObject, //(optional scope)
53266     discardUrl: false,
53267     nocache: false,
53268     text: "Loading...",
53269     timeout: 30,
53270     scripts: false
53271 });
53272 </code></pre>
53273      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53274      * 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.
53275      * @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}
53276      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53277      * @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.
53278      * @return {Roo.ContentPanel} this
53279      */
53280     load : function(){
53281         var um = this.el.getUpdateManager();
53282         um.update.apply(um, arguments);
53283         return this;
53284     },
53285
53286
53287     /**
53288      * 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.
53289      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53290      * @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)
53291      * @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)
53292      * @return {Roo.UpdateManager} The UpdateManager
53293      */
53294     setUrl : function(url, params, loadOnce){
53295         if(this.refreshDelegate){
53296             this.removeListener("activate", this.refreshDelegate);
53297         }
53298         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53299         this.on("activate", this.refreshDelegate);
53300         return this.el.getUpdateManager();
53301     },
53302     
53303     _handleRefresh : function(url, params, loadOnce){
53304         if(!loadOnce || !this.loaded){
53305             var updater = this.el.getUpdateManager();
53306             updater.update(url, params, this._setLoaded.createDelegate(this));
53307         }
53308     },
53309     
53310     _setLoaded : function(){
53311         this.loaded = true;
53312     }, 
53313     
53314     /**
53315      * Returns this panel's id
53316      * @return {String} 
53317      */
53318     getId : function(){
53319         return this.el.id;
53320     },
53321     
53322     /** 
53323      * Returns this panel's element - used by regiosn to add.
53324      * @return {Roo.Element} 
53325      */
53326     getEl : function(){
53327         return this.wrapEl || this.el;
53328     },
53329     
53330     adjustForComponents : function(width, height)
53331     {
53332         //Roo.log('adjustForComponents ');
53333         if(this.resizeEl != this.el){
53334             width -= this.el.getFrameWidth('lr');
53335             height -= this.el.getFrameWidth('tb');
53336         }
53337         if(this.toolbar){
53338             var te = this.toolbar.getEl();
53339             height -= te.getHeight();
53340             te.setWidth(width);
53341         }
53342         if(this.footer){
53343             var te = this.footer.getEl();
53344             Roo.log("footer:" + te.getHeight());
53345             
53346             height -= te.getHeight();
53347             te.setWidth(width);
53348         }
53349         
53350         
53351         if(this.adjustments){
53352             width += this.adjustments[0];
53353             height += this.adjustments[1];
53354         }
53355         return {"width": width, "height": height};
53356     },
53357     
53358     setSize : function(width, height){
53359         if(this.fitToFrame && !this.ignoreResize(width, height)){
53360             if(this.fitContainer && this.resizeEl != this.el){
53361                 this.el.setSize(width, height);
53362             }
53363             var size = this.adjustForComponents(width, height);
53364             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53365             this.fireEvent('resize', this, size.width, size.height);
53366         }
53367     },
53368     
53369     /**
53370      * Returns this panel's title
53371      * @return {String} 
53372      */
53373     getTitle : function(){
53374         return this.title;
53375     },
53376     
53377     /**
53378      * Set this panel's title
53379      * @param {String} title
53380      */
53381     setTitle : function(title){
53382         this.title = title;
53383         if(this.region){
53384             this.region.updatePanelTitle(this, title);
53385         }
53386     },
53387     
53388     /**
53389      * Returns true is this panel was configured to be closable
53390      * @return {Boolean} 
53391      */
53392     isClosable : function(){
53393         return this.closable;
53394     },
53395     
53396     beforeSlide : function(){
53397         this.el.clip();
53398         this.resizeEl.clip();
53399     },
53400     
53401     afterSlide : function(){
53402         this.el.unclip();
53403         this.resizeEl.unclip();
53404     },
53405     
53406     /**
53407      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53408      *   Will fail silently if the {@link #setUrl} method has not been called.
53409      *   This does not activate the panel, just updates its content.
53410      */
53411     refresh : function(){
53412         if(this.refreshDelegate){
53413            this.loaded = false;
53414            this.refreshDelegate();
53415         }
53416     },
53417     
53418     /**
53419      * Destroys this panel
53420      */
53421     destroy : function(){
53422         this.el.removeAllListeners();
53423         var tempEl = document.createElement("span");
53424         tempEl.appendChild(this.el.dom);
53425         tempEl.innerHTML = "";
53426         this.el.remove();
53427         this.el = null;
53428     },
53429     
53430     /**
53431      * form - if the content panel contains a form - this is a reference to it.
53432      * @type {Roo.form.Form}
53433      */
53434     form : false,
53435     /**
53436      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53437      *    This contains a reference to it.
53438      * @type {Roo.View}
53439      */
53440     view : false,
53441     
53442       /**
53443      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53444      * <pre><code>
53445
53446 layout.addxtype({
53447        xtype : 'Form',
53448        items: [ .... ]
53449    }
53450 );
53451
53452 </code></pre>
53453      * @param {Object} cfg Xtype definition of item to add.
53454      */
53455     
53456     addxtype : function(cfg) {
53457         // add form..
53458         if (cfg.xtype.match(/^Form$/)) {
53459             
53460             var el;
53461             //if (this.footer) {
53462             //    el = this.footer.container.insertSibling(false, 'before');
53463             //} else {
53464                 el = this.el.createChild();
53465             //}
53466
53467             this.form = new  Roo.form.Form(cfg);
53468             
53469             
53470             if ( this.form.allItems.length) {
53471                 this.form.render(el.dom);
53472             }
53473             return this.form;
53474         }
53475         // should only have one of theses..
53476         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53477             // views.. should not be just added - used named prop 'view''
53478             
53479             cfg.el = this.el.appendChild(document.createElement("div"));
53480             // factory?
53481             
53482             var ret = new Roo.factory(cfg);
53483              
53484              ret.render && ret.render(false, ''); // render blank..
53485             this.view = ret;
53486             return ret;
53487         }
53488         return false;
53489     }
53490 });
53491
53492 /**
53493  * @class Roo.GridPanel
53494  * @extends Roo.ContentPanel
53495  * @constructor
53496  * Create a new GridPanel.
53497  * @param {Roo.grid.Grid} grid The grid for this panel
53498  * @param {String/Object} config A string to set only the panel's title, or a config object
53499  */
53500 Roo.GridPanel = function(grid, config){
53501     
53502   
53503     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53504         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53505         
53506     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53507     
53508     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53509     
53510     if(this.toolbar){
53511         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53512     }
53513     // xtype created footer. - not sure if will work as we normally have to render first..
53514     if (this.footer && !this.footer.el && this.footer.xtype) {
53515         
53516         this.footer.container = this.grid.getView().getFooterPanel(true);
53517         this.footer.dataSource = this.grid.dataSource;
53518         this.footer = Roo.factory(this.footer, Roo);
53519         
53520     }
53521     
53522     grid.monitorWindowResize = false; // turn off autosizing
53523     grid.autoHeight = false;
53524     grid.autoWidth = false;
53525     this.grid = grid;
53526     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53527 };
53528
53529 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53530     getId : function(){
53531         return this.grid.id;
53532     },
53533     
53534     /**
53535      * Returns the grid for this panel
53536      * @return {Roo.grid.Grid} 
53537      */
53538     getGrid : function(){
53539         return this.grid;    
53540     },
53541     
53542     setSize : function(width, height){
53543         if(!this.ignoreResize(width, height)){
53544             var grid = this.grid;
53545             var size = this.adjustForComponents(width, height);
53546             grid.getGridEl().setSize(size.width, size.height);
53547             grid.autoSize();
53548         }
53549     },
53550     
53551     beforeSlide : function(){
53552         this.grid.getView().scroller.clip();
53553     },
53554     
53555     afterSlide : function(){
53556         this.grid.getView().scroller.unclip();
53557     },
53558     
53559     destroy : function(){
53560         this.grid.destroy();
53561         delete this.grid;
53562         Roo.GridPanel.superclass.destroy.call(this); 
53563     }
53564 });
53565
53566
53567 /**
53568  * @class Roo.NestedLayoutPanel
53569  * @extends Roo.ContentPanel
53570  * @constructor
53571  * Create a new NestedLayoutPanel.
53572  * 
53573  * 
53574  * @param {Roo.BorderLayout} layout The layout for this panel
53575  * @param {String/Object} config A string to set only the title or a config object
53576  */
53577 Roo.NestedLayoutPanel = function(layout, config)
53578 {
53579     // construct with only one argument..
53580     /* FIXME - implement nicer consturctors
53581     if (layout.layout) {
53582         config = layout;
53583         layout = config.layout;
53584         delete config.layout;
53585     }
53586     if (layout.xtype && !layout.getEl) {
53587         // then layout needs constructing..
53588         layout = Roo.factory(layout, Roo);
53589     }
53590     */
53591     
53592     
53593     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53594     
53595     layout.monitorWindowResize = false; // turn off autosizing
53596     this.layout = layout;
53597     this.layout.getEl().addClass("x-layout-nested-layout");
53598     
53599     
53600     
53601     
53602 };
53603
53604 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53605
53606     setSize : function(width, height){
53607         if(!this.ignoreResize(width, height)){
53608             var size = this.adjustForComponents(width, height);
53609             var el = this.layout.getEl();
53610             el.setSize(size.width, size.height);
53611             var touch = el.dom.offsetWidth;
53612             this.layout.layout();
53613             // ie requires a double layout on the first pass
53614             if(Roo.isIE && !this.initialized){
53615                 this.initialized = true;
53616                 this.layout.layout();
53617             }
53618         }
53619     },
53620     
53621     // activate all subpanels if not currently active..
53622     
53623     setActiveState : function(active){
53624         this.active = active;
53625         if(!active){
53626             this.fireEvent("deactivate", this);
53627             return;
53628         }
53629         
53630         this.fireEvent("activate", this);
53631         // not sure if this should happen before or after..
53632         if (!this.layout) {
53633             return; // should not happen..
53634         }
53635         var reg = false;
53636         for (var r in this.layout.regions) {
53637             reg = this.layout.getRegion(r);
53638             if (reg.getActivePanel()) {
53639                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53640                 reg.setActivePanel(reg.getActivePanel());
53641                 continue;
53642             }
53643             if (!reg.panels.length) {
53644                 continue;
53645             }
53646             reg.showPanel(reg.getPanel(0));
53647         }
53648         
53649         
53650         
53651         
53652     },
53653     
53654     /**
53655      * Returns the nested BorderLayout for this panel
53656      * @return {Roo.BorderLayout} 
53657      */
53658     getLayout : function(){
53659         return this.layout;
53660     },
53661     
53662      /**
53663      * Adds a xtype elements to the layout of the nested panel
53664      * <pre><code>
53665
53666 panel.addxtype({
53667        xtype : 'ContentPanel',
53668        region: 'west',
53669        items: [ .... ]
53670    }
53671 );
53672
53673 panel.addxtype({
53674         xtype : 'NestedLayoutPanel',
53675         region: 'west',
53676         layout: {
53677            center: { },
53678            west: { }   
53679         },
53680         items : [ ... list of content panels or nested layout panels.. ]
53681    }
53682 );
53683 </code></pre>
53684      * @param {Object} cfg Xtype definition of item to add.
53685      */
53686     addxtype : function(cfg) {
53687         return this.layout.addxtype(cfg);
53688     
53689     }
53690 });
53691
53692 Roo.ScrollPanel = function(el, config, content){
53693     config = config || {};
53694     config.fitToFrame = true;
53695     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53696     
53697     this.el.dom.style.overflow = "hidden";
53698     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53699     this.el.removeClass("x-layout-inactive-content");
53700     this.el.on("mousewheel", this.onWheel, this);
53701
53702     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53703     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53704     up.unselectable(); down.unselectable();
53705     up.on("click", this.scrollUp, this);
53706     down.on("click", this.scrollDown, this);
53707     up.addClassOnOver("x-scroller-btn-over");
53708     down.addClassOnOver("x-scroller-btn-over");
53709     up.addClassOnClick("x-scroller-btn-click");
53710     down.addClassOnClick("x-scroller-btn-click");
53711     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53712
53713     this.resizeEl = this.el;
53714     this.el = wrap; this.up = up; this.down = down;
53715 };
53716
53717 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53718     increment : 100,
53719     wheelIncrement : 5,
53720     scrollUp : function(){
53721         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53722     },
53723
53724     scrollDown : function(){
53725         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53726     },
53727
53728     afterScroll : function(){
53729         var el = this.resizeEl;
53730         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53731         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53732         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53733     },
53734
53735     setSize : function(){
53736         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53737         this.afterScroll();
53738     },
53739
53740     onWheel : function(e){
53741         var d = e.getWheelDelta();
53742         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53743         this.afterScroll();
53744         e.stopEvent();
53745     },
53746
53747     setContent : function(content, loadScripts){
53748         this.resizeEl.update(content, loadScripts);
53749     }
53750
53751 });
53752
53753
53754
53755
53756
53757
53758
53759
53760
53761 /**
53762  * @class Roo.TreePanel
53763  * @extends Roo.ContentPanel
53764  * @constructor
53765  * Create a new TreePanel. - defaults to fit/scoll contents.
53766  * @param {String/Object} config A string to set only the panel's title, or a config object
53767  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53768  */
53769 Roo.TreePanel = function(config){
53770     var el = config.el;
53771     var tree = config.tree;
53772     delete config.tree; 
53773     delete config.el; // hopefull!
53774     
53775     // wrapper for IE7 strict & safari scroll issue
53776     
53777     var treeEl = el.createChild();
53778     config.resizeEl = treeEl;
53779     
53780     
53781     
53782     Roo.TreePanel.superclass.constructor.call(this, el, config);
53783  
53784  
53785     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53786     //console.log(tree);
53787     this.on('activate', function()
53788     {
53789         if (this.tree.rendered) {
53790             return;
53791         }
53792         //console.log('render tree');
53793         this.tree.render();
53794     });
53795     // this should not be needed.. - it's actually the 'el' that resizes?
53796     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53797     
53798     //this.on('resize',  function (cp, w, h) {
53799     //        this.tree.innerCt.setWidth(w);
53800     //        this.tree.innerCt.setHeight(h);
53801     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53802     //});
53803
53804         
53805     
53806 };
53807
53808 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53809     fitToFrame : true,
53810     autoScroll : true
53811 });
53812
53813
53814
53815
53816
53817
53818
53819
53820
53821
53822
53823 /*
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 /**
53836  * @class Roo.ReaderLayout
53837  * @extends Roo.BorderLayout
53838  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53839  * center region containing two nested regions (a top one for a list view and one for item preview below),
53840  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53841  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53842  * expedites the setup of the overall layout and regions for this common application style.
53843  * Example:
53844  <pre><code>
53845 var reader = new Roo.ReaderLayout();
53846 var CP = Roo.ContentPanel;  // shortcut for adding
53847
53848 reader.beginUpdate();
53849 reader.add("north", new CP("north", "North"));
53850 reader.add("west", new CP("west", {title: "West"}));
53851 reader.add("east", new CP("east", {title: "East"}));
53852
53853 reader.regions.listView.add(new CP("listView", "List"));
53854 reader.regions.preview.add(new CP("preview", "Preview"));
53855 reader.endUpdate();
53856 </code></pre>
53857 * @constructor
53858 * Create a new ReaderLayout
53859 * @param {Object} config Configuration options
53860 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53861 * document.body if omitted)
53862 */
53863 Roo.ReaderLayout = function(config, renderTo){
53864     var c = config || {size:{}};
53865     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53866         north: c.north !== false ? Roo.apply({
53867             split:false,
53868             initialSize: 32,
53869             titlebar: false
53870         }, c.north) : false,
53871         west: c.west !== false ? Roo.apply({
53872             split:true,
53873             initialSize: 200,
53874             minSize: 175,
53875             maxSize: 400,
53876             titlebar: true,
53877             collapsible: true,
53878             animate: true,
53879             margins:{left:5,right:0,bottom:5,top:5},
53880             cmargins:{left:5,right:5,bottom:5,top:5}
53881         }, c.west) : false,
53882         east: c.east !== false ? Roo.apply({
53883             split:true,
53884             initialSize: 200,
53885             minSize: 175,
53886             maxSize: 400,
53887             titlebar: true,
53888             collapsible: true,
53889             animate: true,
53890             margins:{left:0,right:5,bottom:5,top:5},
53891             cmargins:{left:5,right:5,bottom:5,top:5}
53892         }, c.east) : false,
53893         center: Roo.apply({
53894             tabPosition: 'top',
53895             autoScroll:false,
53896             closeOnTab: true,
53897             titlebar:false,
53898             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53899         }, c.center)
53900     });
53901
53902     this.el.addClass('x-reader');
53903
53904     this.beginUpdate();
53905
53906     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53907         south: c.preview !== false ? Roo.apply({
53908             split:true,
53909             initialSize: 200,
53910             minSize: 100,
53911             autoScroll:true,
53912             collapsible:true,
53913             titlebar: true,
53914             cmargins:{top:5,left:0, right:0, bottom:0}
53915         }, c.preview) : false,
53916         center: Roo.apply({
53917             autoScroll:false,
53918             titlebar:false,
53919             minHeight:200
53920         }, c.listView)
53921     });
53922     this.add('center', new Roo.NestedLayoutPanel(inner,
53923             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53924
53925     this.endUpdate();
53926
53927     this.regions.preview = inner.getRegion('south');
53928     this.regions.listView = inner.getRegion('center');
53929 };
53930
53931 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53932  * Based on:
53933  * Ext JS Library 1.1.1
53934  * Copyright(c) 2006-2007, Ext JS, LLC.
53935  *
53936  * Originally Released Under LGPL - original licence link has changed is not relivant.
53937  *
53938  * Fork - LGPL
53939  * <script type="text/javascript">
53940  */
53941  
53942 /**
53943  * @class Roo.grid.Grid
53944  * @extends Roo.util.Observable
53945  * This class represents the primary interface of a component based grid control.
53946  * <br><br>Usage:<pre><code>
53947  var grid = new Roo.grid.Grid("my-container-id", {
53948      ds: myDataStore,
53949      cm: myColModel,
53950      selModel: mySelectionModel,
53951      autoSizeColumns: true,
53952      monitorWindowResize: false,
53953      trackMouseOver: true
53954  });
53955  // set any options
53956  grid.render();
53957  * </code></pre>
53958  * <b>Common Problems:</b><br/>
53959  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
53960  * element will correct this<br/>
53961  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
53962  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
53963  * are unpredictable.<br/>
53964  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
53965  * grid to calculate dimensions/offsets.<br/>
53966   * @constructor
53967  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
53968  * The container MUST have some type of size defined for the grid to fill. The container will be
53969  * automatically set to position relative if it isn't already.
53970  * @param {Object} config A config object that sets properties on this grid.
53971  */
53972 Roo.grid.Grid = function(container, config){
53973         // initialize the container
53974         this.container = Roo.get(container);
53975         this.container.update("");
53976         this.container.setStyle("overflow", "hidden");
53977     this.container.addClass('x-grid-container');
53978
53979     this.id = this.container.id;
53980
53981     Roo.apply(this, config);
53982     // check and correct shorthanded configs
53983     if(this.ds){
53984         this.dataSource = this.ds;
53985         delete this.ds;
53986     }
53987     if(this.cm){
53988         this.colModel = this.cm;
53989         delete this.cm;
53990     }
53991     if(this.sm){
53992         this.selModel = this.sm;
53993         delete this.sm;
53994     }
53995
53996     if (this.selModel) {
53997         this.selModel = Roo.factory(this.selModel, Roo.grid);
53998         this.sm = this.selModel;
53999         this.sm.xmodule = this.xmodule || false;
54000     }
54001     if (typeof(this.colModel.config) == 'undefined') {
54002         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54003         this.cm = this.colModel;
54004         this.cm.xmodule = this.xmodule || false;
54005     }
54006     if (this.dataSource) {
54007         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54008         this.ds = this.dataSource;
54009         this.ds.xmodule = this.xmodule || false;
54010          
54011     }
54012     
54013     
54014     
54015     if(this.width){
54016         this.container.setWidth(this.width);
54017     }
54018
54019     if(this.height){
54020         this.container.setHeight(this.height);
54021     }
54022     /** @private */
54023         this.addEvents({
54024         // raw events
54025         /**
54026          * @event click
54027          * The raw click event for the entire grid.
54028          * @param {Roo.EventObject} e
54029          */
54030         "click" : true,
54031         /**
54032          * @event dblclick
54033          * The raw dblclick event for the entire grid.
54034          * @param {Roo.EventObject} e
54035          */
54036         "dblclick" : true,
54037         /**
54038          * @event contextmenu
54039          * The raw contextmenu event for the entire grid.
54040          * @param {Roo.EventObject} e
54041          */
54042         "contextmenu" : true,
54043         /**
54044          * @event mousedown
54045          * The raw mousedown event for the entire grid.
54046          * @param {Roo.EventObject} e
54047          */
54048         "mousedown" : true,
54049         /**
54050          * @event mouseup
54051          * The raw mouseup event for the entire grid.
54052          * @param {Roo.EventObject} e
54053          */
54054         "mouseup" : true,
54055         /**
54056          * @event mouseover
54057          * The raw mouseover event for the entire grid.
54058          * @param {Roo.EventObject} e
54059          */
54060         "mouseover" : true,
54061         /**
54062          * @event mouseout
54063          * The raw mouseout event for the entire grid.
54064          * @param {Roo.EventObject} e
54065          */
54066         "mouseout" : true,
54067         /**
54068          * @event keypress
54069          * The raw keypress event for the entire grid.
54070          * @param {Roo.EventObject} e
54071          */
54072         "keypress" : true,
54073         /**
54074          * @event keydown
54075          * The raw keydown event for the entire grid.
54076          * @param {Roo.EventObject} e
54077          */
54078         "keydown" : true,
54079
54080         // custom events
54081
54082         /**
54083          * @event cellclick
54084          * Fires when a cell is clicked
54085          * @param {Grid} this
54086          * @param {Number} rowIndex
54087          * @param {Number} columnIndex
54088          * @param {Roo.EventObject} e
54089          */
54090         "cellclick" : true,
54091         /**
54092          * @event celldblclick
54093          * Fires when a cell is double clicked
54094          * @param {Grid} this
54095          * @param {Number} rowIndex
54096          * @param {Number} columnIndex
54097          * @param {Roo.EventObject} e
54098          */
54099         "celldblclick" : true,
54100         /**
54101          * @event rowclick
54102          * Fires when a row is clicked
54103          * @param {Grid} this
54104          * @param {Number} rowIndex
54105          * @param {Roo.EventObject} e
54106          */
54107         "rowclick" : true,
54108         /**
54109          * @event rowdblclick
54110          * Fires when a row is double clicked
54111          * @param {Grid} this
54112          * @param {Number} rowIndex
54113          * @param {Roo.EventObject} e
54114          */
54115         "rowdblclick" : true,
54116         /**
54117          * @event headerclick
54118          * Fires when a header is clicked
54119          * @param {Grid} this
54120          * @param {Number} columnIndex
54121          * @param {Roo.EventObject} e
54122          */
54123         "headerclick" : true,
54124         /**
54125          * @event headerdblclick
54126          * Fires when a header cell is double clicked
54127          * @param {Grid} this
54128          * @param {Number} columnIndex
54129          * @param {Roo.EventObject} e
54130          */
54131         "headerdblclick" : true,
54132         /**
54133          * @event rowcontextmenu
54134          * Fires when a row is right clicked
54135          * @param {Grid} this
54136          * @param {Number} rowIndex
54137          * @param {Roo.EventObject} e
54138          */
54139         "rowcontextmenu" : true,
54140         /**
54141          * @event cellcontextmenu
54142          * Fires when a cell is right clicked
54143          * @param {Grid} this
54144          * @param {Number} rowIndex
54145          * @param {Number} cellIndex
54146          * @param {Roo.EventObject} e
54147          */
54148          "cellcontextmenu" : true,
54149         /**
54150          * @event headercontextmenu
54151          * Fires when a header is right clicked
54152          * @param {Grid} this
54153          * @param {Number} columnIndex
54154          * @param {Roo.EventObject} e
54155          */
54156         "headercontextmenu" : true,
54157         /**
54158          * @event bodyscroll
54159          * Fires when the body element is scrolled
54160          * @param {Number} scrollLeft
54161          * @param {Number} scrollTop
54162          */
54163         "bodyscroll" : true,
54164         /**
54165          * @event columnresize
54166          * Fires when the user resizes a column
54167          * @param {Number} columnIndex
54168          * @param {Number} newSize
54169          */
54170         "columnresize" : true,
54171         /**
54172          * @event columnmove
54173          * Fires when the user moves a column
54174          * @param {Number} oldIndex
54175          * @param {Number} newIndex
54176          */
54177         "columnmove" : true,
54178         /**
54179          * @event startdrag
54180          * Fires when row(s) start being dragged
54181          * @param {Grid} this
54182          * @param {Roo.GridDD} dd The drag drop object
54183          * @param {event} e The raw browser event
54184          */
54185         "startdrag" : true,
54186         /**
54187          * @event enddrag
54188          * Fires when a drag operation is complete
54189          * @param {Grid} this
54190          * @param {Roo.GridDD} dd The drag drop object
54191          * @param {event} e The raw browser event
54192          */
54193         "enddrag" : true,
54194         /**
54195          * @event dragdrop
54196          * Fires when dragged row(s) are dropped on a valid DD target
54197          * @param {Grid} this
54198          * @param {Roo.GridDD} dd The drag drop object
54199          * @param {String} targetId The target drag drop object
54200          * @param {event} e The raw browser event
54201          */
54202         "dragdrop" : true,
54203         /**
54204          * @event dragover
54205          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54206          * @param {Grid} this
54207          * @param {Roo.GridDD} dd The drag drop object
54208          * @param {String} targetId The target drag drop object
54209          * @param {event} e The raw browser event
54210          */
54211         "dragover" : true,
54212         /**
54213          * @event dragenter
54214          *  Fires when the dragged row(s) first cross another DD target while being dragged
54215          * @param {Grid} this
54216          * @param {Roo.GridDD} dd The drag drop object
54217          * @param {String} targetId The target drag drop object
54218          * @param {event} e The raw browser event
54219          */
54220         "dragenter" : true,
54221         /**
54222          * @event dragout
54223          * Fires when the dragged row(s) leave another DD target while being dragged
54224          * @param {Grid} this
54225          * @param {Roo.GridDD} dd The drag drop object
54226          * @param {String} targetId The target drag drop object
54227          * @param {event} e The raw browser event
54228          */
54229         "dragout" : true,
54230         /**
54231          * @event rowclass
54232          * Fires when a row is rendered, so you can change add a style to it.
54233          * @param {GridView} gridview   The grid view
54234          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54235          */
54236         'rowclass' : true,
54237
54238         /**
54239          * @event render
54240          * Fires when the grid is rendered
54241          * @param {Grid} grid
54242          */
54243         'render' : true
54244     });
54245
54246     Roo.grid.Grid.superclass.constructor.call(this);
54247 };
54248 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54249     
54250     /**
54251      * @cfg {String} ddGroup - drag drop group.
54252      */
54253
54254     /**
54255      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54256      */
54257     minColumnWidth : 25,
54258
54259     /**
54260      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54261      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54262      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54263      */
54264     autoSizeColumns : false,
54265
54266     /**
54267      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54268      */
54269     autoSizeHeaders : true,
54270
54271     /**
54272      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54273      */
54274     monitorWindowResize : true,
54275
54276     /**
54277      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54278      * rows measured to get a columns size. Default is 0 (all rows).
54279      */
54280     maxRowsToMeasure : 0,
54281
54282     /**
54283      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54284      */
54285     trackMouseOver : true,
54286
54287     /**
54288     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54289     */
54290     
54291     /**
54292     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54293     */
54294     enableDragDrop : false,
54295     
54296     /**
54297     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54298     */
54299     enableColumnMove : true,
54300     
54301     /**
54302     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54303     */
54304     enableColumnHide : true,
54305     
54306     /**
54307     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54308     */
54309     enableRowHeightSync : false,
54310     
54311     /**
54312     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54313     */
54314     stripeRows : true,
54315     
54316     /**
54317     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54318     */
54319     autoHeight : false,
54320
54321     /**
54322      * @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.
54323      */
54324     autoExpandColumn : false,
54325
54326     /**
54327     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54328     * Default is 50.
54329     */
54330     autoExpandMin : 50,
54331
54332     /**
54333     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54334     */
54335     autoExpandMax : 1000,
54336
54337     /**
54338     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54339     */
54340     view : null,
54341
54342     /**
54343     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54344     */
54345     loadMask : false,
54346     /**
54347     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54348     */
54349     dropTarget: false,
54350     
54351    
54352     
54353     // private
54354     rendered : false,
54355
54356     /**
54357     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54358     * of a fixed width. Default is false.
54359     */
54360     /**
54361     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54362     */
54363     /**
54364      * Called once after all setup has been completed and the grid is ready to be rendered.
54365      * @return {Roo.grid.Grid} this
54366      */
54367     render : function()
54368     {
54369         var c = this.container;
54370         // try to detect autoHeight/width mode
54371         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54372             this.autoHeight = true;
54373         }
54374         var view = this.getView();
54375         view.init(this);
54376
54377         c.on("click", this.onClick, this);
54378         c.on("dblclick", this.onDblClick, this);
54379         c.on("contextmenu", this.onContextMenu, this);
54380         c.on("keydown", this.onKeyDown, this);
54381         if (Roo.isTouch) {
54382             c.on("touchstart", this.onTouchStart, this);
54383         }
54384
54385         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54386
54387         this.getSelectionModel().init(this);
54388
54389         view.render();
54390
54391         if(this.loadMask){
54392             this.loadMask = new Roo.LoadMask(this.container,
54393                     Roo.apply({store:this.dataSource}, this.loadMask));
54394         }
54395         
54396         
54397         if (this.toolbar && this.toolbar.xtype) {
54398             this.toolbar.container = this.getView().getHeaderPanel(true);
54399             this.toolbar = new Roo.Toolbar(this.toolbar);
54400         }
54401         if (this.footer && this.footer.xtype) {
54402             this.footer.dataSource = this.getDataSource();
54403             this.footer.container = this.getView().getFooterPanel(true);
54404             this.footer = Roo.factory(this.footer, Roo);
54405         }
54406         if (this.dropTarget && this.dropTarget.xtype) {
54407             delete this.dropTarget.xtype;
54408             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54409         }
54410         
54411         
54412         this.rendered = true;
54413         this.fireEvent('render', this);
54414         return this;
54415     },
54416
54417         /**
54418          * Reconfigures the grid to use a different Store and Column Model.
54419          * The View will be bound to the new objects and refreshed.
54420          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54421          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54422          */
54423     reconfigure : function(dataSource, colModel){
54424         if(this.loadMask){
54425             this.loadMask.destroy();
54426             this.loadMask = new Roo.LoadMask(this.container,
54427                     Roo.apply({store:dataSource}, this.loadMask));
54428         }
54429         this.view.bind(dataSource, colModel);
54430         this.dataSource = dataSource;
54431         this.colModel = colModel;
54432         this.view.refresh(true);
54433     },
54434
54435     // private
54436     onKeyDown : function(e){
54437         this.fireEvent("keydown", e);
54438     },
54439
54440     /**
54441      * Destroy this grid.
54442      * @param {Boolean} removeEl True to remove the element
54443      */
54444     destroy : function(removeEl, keepListeners){
54445         if(this.loadMask){
54446             this.loadMask.destroy();
54447         }
54448         var c = this.container;
54449         c.removeAllListeners();
54450         this.view.destroy();
54451         this.colModel.purgeListeners();
54452         if(!keepListeners){
54453             this.purgeListeners();
54454         }
54455         c.update("");
54456         if(removeEl === true){
54457             c.remove();
54458         }
54459     },
54460
54461     // private
54462     processEvent : function(name, e){
54463         // does this fire select???
54464         //Roo.log('grid:processEvent '  + name);
54465         
54466         if (name != 'touchstart' ) {
54467             this.fireEvent(name, e);    
54468         }
54469         
54470         var t = e.getTarget();
54471         var v = this.view;
54472         var header = v.findHeaderIndex(t);
54473         if(header !== false){
54474             var ename = name == 'touchstart' ? 'click' : name;
54475              
54476             this.fireEvent("header" + ename, this, header, e);
54477         }else{
54478             var row = v.findRowIndex(t);
54479             var cell = v.findCellIndex(t);
54480             if (name == 'touchstart') {
54481                 // first touch is always a click.
54482                 // hopefull this happens after selection is updated.?
54483                 name = false;
54484                 
54485                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54486                     var cs = this.selModel.getSelectedCell();
54487                     if (row == cs[0] && cell == cs[1]){
54488                         name = 'dblclick';
54489                     }
54490                 }
54491                 if (typeof(this.selModel.getSelections) != 'undefined') {
54492                     var cs = this.selModel.getSelections();
54493                     var ds = this.dataSource;
54494                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54495                         name = 'dblclick';
54496                     }
54497                 }
54498                 if (!name) {
54499                     return;
54500                 }
54501             }
54502             
54503             
54504             if(row !== false){
54505                 this.fireEvent("row" + name, this, row, e);
54506                 if(cell !== false){
54507                     this.fireEvent("cell" + name, this, row, cell, e);
54508                 }
54509             }
54510         }
54511     },
54512
54513     // private
54514     onClick : function(e){
54515         this.processEvent("click", e);
54516     },
54517    // private
54518     onTouchStart : function(e){
54519         this.processEvent("touchstart", e);
54520     },
54521
54522     // private
54523     onContextMenu : function(e, t){
54524         this.processEvent("contextmenu", e);
54525     },
54526
54527     // private
54528     onDblClick : function(e){
54529         this.processEvent("dblclick", e);
54530     },
54531
54532     // private
54533     walkCells : function(row, col, step, fn, scope){
54534         var cm = this.colModel, clen = cm.getColumnCount();
54535         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54536         if(step < 0){
54537             if(col < 0){
54538                 row--;
54539                 first = false;
54540             }
54541             while(row >= 0){
54542                 if(!first){
54543                     col = clen-1;
54544                 }
54545                 first = false;
54546                 while(col >= 0){
54547                     if(fn.call(scope || this, row, col, cm) === true){
54548                         return [row, col];
54549                     }
54550                     col--;
54551                 }
54552                 row--;
54553             }
54554         } else {
54555             if(col >= clen){
54556                 row++;
54557                 first = false;
54558             }
54559             while(row < rlen){
54560                 if(!first){
54561                     col = 0;
54562                 }
54563                 first = false;
54564                 while(col < clen){
54565                     if(fn.call(scope || this, row, col, cm) === true){
54566                         return [row, col];
54567                     }
54568                     col++;
54569                 }
54570                 row++;
54571             }
54572         }
54573         return null;
54574     },
54575
54576     // private
54577     getSelections : function(){
54578         return this.selModel.getSelections();
54579     },
54580
54581     /**
54582      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54583      * but if manual update is required this method will initiate it.
54584      */
54585     autoSize : function(){
54586         if(this.rendered){
54587             this.view.layout();
54588             if(this.view.adjustForScroll){
54589                 this.view.adjustForScroll();
54590             }
54591         }
54592     },
54593
54594     /**
54595      * Returns the grid's underlying element.
54596      * @return {Element} The element
54597      */
54598     getGridEl : function(){
54599         return this.container;
54600     },
54601
54602     // private for compatibility, overridden by editor grid
54603     stopEditing : function(){},
54604
54605     /**
54606      * Returns the grid's SelectionModel.
54607      * @return {SelectionModel}
54608      */
54609     getSelectionModel : function(){
54610         if(!this.selModel){
54611             this.selModel = new Roo.grid.RowSelectionModel();
54612         }
54613         return this.selModel;
54614     },
54615
54616     /**
54617      * Returns the grid's DataSource.
54618      * @return {DataSource}
54619      */
54620     getDataSource : function(){
54621         return this.dataSource;
54622     },
54623
54624     /**
54625      * Returns the grid's ColumnModel.
54626      * @return {ColumnModel}
54627      */
54628     getColumnModel : function(){
54629         return this.colModel;
54630     },
54631
54632     /**
54633      * Returns the grid's GridView object.
54634      * @return {GridView}
54635      */
54636     getView : function(){
54637         if(!this.view){
54638             this.view = new Roo.grid.GridView(this.viewConfig);
54639         }
54640         return this.view;
54641     },
54642     /**
54643      * Called to get grid's drag proxy text, by default returns this.ddText.
54644      * @return {String}
54645      */
54646     getDragDropText : function(){
54647         var count = this.selModel.getCount();
54648         return String.format(this.ddText, count, count == 1 ? '' : 's');
54649     }
54650 });
54651 /**
54652  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54653  * %0 is replaced with the number of selected rows.
54654  * @type String
54655  */
54656 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54657  * Based on:
54658  * Ext JS Library 1.1.1
54659  * Copyright(c) 2006-2007, Ext JS, LLC.
54660  *
54661  * Originally Released Under LGPL - original licence link has changed is not relivant.
54662  *
54663  * Fork - LGPL
54664  * <script type="text/javascript">
54665  */
54666  
54667 Roo.grid.AbstractGridView = function(){
54668         this.grid = null;
54669         
54670         this.events = {
54671             "beforerowremoved" : true,
54672             "beforerowsinserted" : true,
54673             "beforerefresh" : true,
54674             "rowremoved" : true,
54675             "rowsinserted" : true,
54676             "rowupdated" : true,
54677             "refresh" : true
54678         };
54679     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54680 };
54681
54682 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54683     rowClass : "x-grid-row",
54684     cellClass : "x-grid-cell",
54685     tdClass : "x-grid-td",
54686     hdClass : "x-grid-hd",
54687     splitClass : "x-grid-hd-split",
54688     
54689     init: function(grid){
54690         this.grid = grid;
54691                 var cid = this.grid.getGridEl().id;
54692         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54693         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54694         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54695         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54696         },
54697         
54698     getColumnRenderers : function(){
54699         var renderers = [];
54700         var cm = this.grid.colModel;
54701         var colCount = cm.getColumnCount();
54702         for(var i = 0; i < colCount; i++){
54703             renderers[i] = cm.getRenderer(i);
54704         }
54705         return renderers;
54706     },
54707     
54708     getColumnIds : function(){
54709         var ids = [];
54710         var cm = this.grid.colModel;
54711         var colCount = cm.getColumnCount();
54712         for(var i = 0; i < colCount; i++){
54713             ids[i] = cm.getColumnId(i);
54714         }
54715         return ids;
54716     },
54717     
54718     getDataIndexes : function(){
54719         if(!this.indexMap){
54720             this.indexMap = this.buildIndexMap();
54721         }
54722         return this.indexMap.colToData;
54723     },
54724     
54725     getColumnIndexByDataIndex : function(dataIndex){
54726         if(!this.indexMap){
54727             this.indexMap = this.buildIndexMap();
54728         }
54729         return this.indexMap.dataToCol[dataIndex];
54730     },
54731     
54732     /**
54733      * Set a css style for a column dynamically. 
54734      * @param {Number} colIndex The index of the column
54735      * @param {String} name The css property name
54736      * @param {String} value The css value
54737      */
54738     setCSSStyle : function(colIndex, name, value){
54739         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54740         Roo.util.CSS.updateRule(selector, name, value);
54741     },
54742     
54743     generateRules : function(cm){
54744         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54745         Roo.util.CSS.removeStyleSheet(rulesId);
54746         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54747             var cid = cm.getColumnId(i);
54748             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54749                          this.tdSelector, cid, " {\n}\n",
54750                          this.hdSelector, cid, " {\n}\n",
54751                          this.splitSelector, cid, " {\n}\n");
54752         }
54753         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54754     }
54755 });/*
54756  * Based on:
54757  * Ext JS Library 1.1.1
54758  * Copyright(c) 2006-2007, Ext JS, LLC.
54759  *
54760  * Originally Released Under LGPL - original licence link has changed is not relivant.
54761  *
54762  * Fork - LGPL
54763  * <script type="text/javascript">
54764  */
54765
54766 // private
54767 // This is a support class used internally by the Grid components
54768 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54769     this.grid = grid;
54770     this.view = grid.getView();
54771     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54772     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54773     if(hd2){
54774         this.setHandleElId(Roo.id(hd));
54775         this.setOuterHandleElId(Roo.id(hd2));
54776     }
54777     this.scroll = false;
54778 };
54779 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54780     maxDragWidth: 120,
54781     getDragData : function(e){
54782         var t = Roo.lib.Event.getTarget(e);
54783         var h = this.view.findHeaderCell(t);
54784         if(h){
54785             return {ddel: h.firstChild, header:h};
54786         }
54787         return false;
54788     },
54789
54790     onInitDrag : function(e){
54791         this.view.headersDisabled = true;
54792         var clone = this.dragData.ddel.cloneNode(true);
54793         clone.id = Roo.id();
54794         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54795         this.proxy.update(clone);
54796         return true;
54797     },
54798
54799     afterValidDrop : function(){
54800         var v = this.view;
54801         setTimeout(function(){
54802             v.headersDisabled = false;
54803         }, 50);
54804     },
54805
54806     afterInvalidDrop : function(){
54807         var v = this.view;
54808         setTimeout(function(){
54809             v.headersDisabled = false;
54810         }, 50);
54811     }
54812 });
54813 /*
54814  * Based on:
54815  * Ext JS Library 1.1.1
54816  * Copyright(c) 2006-2007, Ext JS, LLC.
54817  *
54818  * Originally Released Under LGPL - original licence link has changed is not relivant.
54819  *
54820  * Fork - LGPL
54821  * <script type="text/javascript">
54822  */
54823 // private
54824 // This is a support class used internally by the Grid components
54825 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54826     this.grid = grid;
54827     this.view = grid.getView();
54828     // split the proxies so they don't interfere with mouse events
54829     this.proxyTop = Roo.DomHelper.append(document.body, {
54830         cls:"col-move-top", html:"&#160;"
54831     }, true);
54832     this.proxyBottom = Roo.DomHelper.append(document.body, {
54833         cls:"col-move-bottom", html:"&#160;"
54834     }, true);
54835     this.proxyTop.hide = this.proxyBottom.hide = function(){
54836         this.setLeftTop(-100,-100);
54837         this.setStyle("visibility", "hidden");
54838     };
54839     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54840     // temporarily disabled
54841     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54842     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54843 };
54844 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54845     proxyOffsets : [-4, -9],
54846     fly: Roo.Element.fly,
54847
54848     getTargetFromEvent : function(e){
54849         var t = Roo.lib.Event.getTarget(e);
54850         var cindex = this.view.findCellIndex(t);
54851         if(cindex !== false){
54852             return this.view.getHeaderCell(cindex);
54853         }
54854         return null;
54855     },
54856
54857     nextVisible : function(h){
54858         var v = this.view, cm = this.grid.colModel;
54859         h = h.nextSibling;
54860         while(h){
54861             if(!cm.isHidden(v.getCellIndex(h))){
54862                 return h;
54863             }
54864             h = h.nextSibling;
54865         }
54866         return null;
54867     },
54868
54869     prevVisible : function(h){
54870         var v = this.view, cm = this.grid.colModel;
54871         h = h.prevSibling;
54872         while(h){
54873             if(!cm.isHidden(v.getCellIndex(h))){
54874                 return h;
54875             }
54876             h = h.prevSibling;
54877         }
54878         return null;
54879     },
54880
54881     positionIndicator : function(h, n, e){
54882         var x = Roo.lib.Event.getPageX(e);
54883         var r = Roo.lib.Dom.getRegion(n.firstChild);
54884         var px, pt, py = r.top + this.proxyOffsets[1];
54885         if((r.right - x) <= (r.right-r.left)/2){
54886             px = r.right+this.view.borderWidth;
54887             pt = "after";
54888         }else{
54889             px = r.left;
54890             pt = "before";
54891         }
54892         var oldIndex = this.view.getCellIndex(h);
54893         var newIndex = this.view.getCellIndex(n);
54894
54895         if(this.grid.colModel.isFixed(newIndex)){
54896             return false;
54897         }
54898
54899         var locked = this.grid.colModel.isLocked(newIndex);
54900
54901         if(pt == "after"){
54902             newIndex++;
54903         }
54904         if(oldIndex < newIndex){
54905             newIndex--;
54906         }
54907         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54908             return false;
54909         }
54910         px +=  this.proxyOffsets[0];
54911         this.proxyTop.setLeftTop(px, py);
54912         this.proxyTop.show();
54913         if(!this.bottomOffset){
54914             this.bottomOffset = this.view.mainHd.getHeight();
54915         }
54916         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54917         this.proxyBottom.show();
54918         return pt;
54919     },
54920
54921     onNodeEnter : function(n, dd, e, data){
54922         if(data.header != n){
54923             this.positionIndicator(data.header, n, e);
54924         }
54925     },
54926
54927     onNodeOver : function(n, dd, e, data){
54928         var result = false;
54929         if(data.header != n){
54930             result = this.positionIndicator(data.header, n, e);
54931         }
54932         if(!result){
54933             this.proxyTop.hide();
54934             this.proxyBottom.hide();
54935         }
54936         return result ? this.dropAllowed : this.dropNotAllowed;
54937     },
54938
54939     onNodeOut : function(n, dd, e, data){
54940         this.proxyTop.hide();
54941         this.proxyBottom.hide();
54942     },
54943
54944     onNodeDrop : function(n, dd, e, data){
54945         var h = data.header;
54946         if(h != n){
54947             var cm = this.grid.colModel;
54948             var x = Roo.lib.Event.getPageX(e);
54949             var r = Roo.lib.Dom.getRegion(n.firstChild);
54950             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54951             var oldIndex = this.view.getCellIndex(h);
54952             var newIndex = this.view.getCellIndex(n);
54953             var locked = cm.isLocked(newIndex);
54954             if(pt == "after"){
54955                 newIndex++;
54956             }
54957             if(oldIndex < newIndex){
54958                 newIndex--;
54959             }
54960             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
54961                 return false;
54962             }
54963             cm.setLocked(oldIndex, locked, true);
54964             cm.moveColumn(oldIndex, newIndex);
54965             this.grid.fireEvent("columnmove", oldIndex, newIndex);
54966             return true;
54967         }
54968         return false;
54969     }
54970 });
54971 /*
54972  * Based on:
54973  * Ext JS Library 1.1.1
54974  * Copyright(c) 2006-2007, Ext JS, LLC.
54975  *
54976  * Originally Released Under LGPL - original licence link has changed is not relivant.
54977  *
54978  * Fork - LGPL
54979  * <script type="text/javascript">
54980  */
54981   
54982 /**
54983  * @class Roo.grid.GridView
54984  * @extends Roo.util.Observable
54985  *
54986  * @constructor
54987  * @param {Object} config
54988  */
54989 Roo.grid.GridView = function(config){
54990     Roo.grid.GridView.superclass.constructor.call(this);
54991     this.el = null;
54992
54993     Roo.apply(this, config);
54994 };
54995
54996 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
54997
54998     unselectable :  'unselectable="on"',
54999     unselectableCls :  'x-unselectable',
55000     
55001     
55002     rowClass : "x-grid-row",
55003
55004     cellClass : "x-grid-col",
55005
55006     tdClass : "x-grid-td",
55007
55008     hdClass : "x-grid-hd",
55009
55010     splitClass : "x-grid-split",
55011
55012     sortClasses : ["sort-asc", "sort-desc"],
55013
55014     enableMoveAnim : false,
55015
55016     hlColor: "C3DAF9",
55017
55018     dh : Roo.DomHelper,
55019
55020     fly : Roo.Element.fly,
55021
55022     css : Roo.util.CSS,
55023
55024     borderWidth: 1,
55025
55026     splitOffset: 3,
55027
55028     scrollIncrement : 22,
55029
55030     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55031
55032     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55033
55034     bind : function(ds, cm){
55035         if(this.ds){
55036             this.ds.un("load", this.onLoad, this);
55037             this.ds.un("datachanged", this.onDataChange, this);
55038             this.ds.un("add", this.onAdd, this);
55039             this.ds.un("remove", this.onRemove, this);
55040             this.ds.un("update", this.onUpdate, this);
55041             this.ds.un("clear", this.onClear, this);
55042         }
55043         if(ds){
55044             ds.on("load", this.onLoad, this);
55045             ds.on("datachanged", this.onDataChange, this);
55046             ds.on("add", this.onAdd, this);
55047             ds.on("remove", this.onRemove, this);
55048             ds.on("update", this.onUpdate, this);
55049             ds.on("clear", this.onClear, this);
55050         }
55051         this.ds = ds;
55052
55053         if(this.cm){
55054             this.cm.un("widthchange", this.onColWidthChange, this);
55055             this.cm.un("headerchange", this.onHeaderChange, this);
55056             this.cm.un("hiddenchange", this.onHiddenChange, this);
55057             this.cm.un("columnmoved", this.onColumnMove, this);
55058             this.cm.un("columnlockchange", this.onColumnLock, this);
55059         }
55060         if(cm){
55061             this.generateRules(cm);
55062             cm.on("widthchange", this.onColWidthChange, this);
55063             cm.on("headerchange", this.onHeaderChange, this);
55064             cm.on("hiddenchange", this.onHiddenChange, this);
55065             cm.on("columnmoved", this.onColumnMove, this);
55066             cm.on("columnlockchange", this.onColumnLock, this);
55067         }
55068         this.cm = cm;
55069     },
55070
55071     init: function(grid){
55072         Roo.grid.GridView.superclass.init.call(this, grid);
55073
55074         this.bind(grid.dataSource, grid.colModel);
55075
55076         grid.on("headerclick", this.handleHeaderClick, this);
55077
55078         if(grid.trackMouseOver){
55079             grid.on("mouseover", this.onRowOver, this);
55080             grid.on("mouseout", this.onRowOut, this);
55081         }
55082         grid.cancelTextSelection = function(){};
55083         this.gridId = grid.id;
55084
55085         var tpls = this.templates || {};
55086
55087         if(!tpls.master){
55088             tpls.master = new Roo.Template(
55089                '<div class="x-grid" hidefocus="true">',
55090                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55091                   '<div class="x-grid-topbar"></div>',
55092                   '<div class="x-grid-scroller"><div></div></div>',
55093                   '<div class="x-grid-locked">',
55094                       '<div class="x-grid-header">{lockedHeader}</div>',
55095                       '<div class="x-grid-body">{lockedBody}</div>',
55096                   "</div>",
55097                   '<div class="x-grid-viewport">',
55098                       '<div class="x-grid-header">{header}</div>',
55099                       '<div class="x-grid-body">{body}</div>',
55100                   "</div>",
55101                   '<div class="x-grid-bottombar"></div>',
55102                  
55103                   '<div class="x-grid-resize-proxy">&#160;</div>',
55104                "</div>"
55105             );
55106             tpls.master.disableformats = true;
55107         }
55108
55109         if(!tpls.header){
55110             tpls.header = new Roo.Template(
55111                '<table border="0" cellspacing="0" cellpadding="0">',
55112                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55113                "</table>{splits}"
55114             );
55115             tpls.header.disableformats = true;
55116         }
55117         tpls.header.compile();
55118
55119         if(!tpls.hcell){
55120             tpls.hcell = new Roo.Template(
55121                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55122                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55123                 "</div></td>"
55124              );
55125              tpls.hcell.disableFormats = true;
55126         }
55127         tpls.hcell.compile();
55128
55129         if(!tpls.hsplit){
55130             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55131                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55132             tpls.hsplit.disableFormats = true;
55133         }
55134         tpls.hsplit.compile();
55135
55136         if(!tpls.body){
55137             tpls.body = new Roo.Template(
55138                '<table border="0" cellspacing="0" cellpadding="0">',
55139                "<tbody>{rows}</tbody>",
55140                "</table>"
55141             );
55142             tpls.body.disableFormats = true;
55143         }
55144         tpls.body.compile();
55145
55146         if(!tpls.row){
55147             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55148             tpls.row.disableFormats = true;
55149         }
55150         tpls.row.compile();
55151
55152         if(!tpls.cell){
55153             tpls.cell = new Roo.Template(
55154                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55155                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55156                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55157                 "</td>"
55158             );
55159             tpls.cell.disableFormats = true;
55160         }
55161         tpls.cell.compile();
55162
55163         this.templates = tpls;
55164     },
55165
55166     // remap these for backwards compat
55167     onColWidthChange : function(){
55168         this.updateColumns.apply(this, arguments);
55169     },
55170     onHeaderChange : function(){
55171         this.updateHeaders.apply(this, arguments);
55172     }, 
55173     onHiddenChange : function(){
55174         this.handleHiddenChange.apply(this, arguments);
55175     },
55176     onColumnMove : function(){
55177         this.handleColumnMove.apply(this, arguments);
55178     },
55179     onColumnLock : function(){
55180         this.handleLockChange.apply(this, arguments);
55181     },
55182
55183     onDataChange : function(){
55184         this.refresh();
55185         this.updateHeaderSortState();
55186     },
55187
55188     onClear : function(){
55189         this.refresh();
55190     },
55191
55192     onUpdate : function(ds, record){
55193         this.refreshRow(record);
55194     },
55195
55196     refreshRow : function(record){
55197         var ds = this.ds, index;
55198         if(typeof record == 'number'){
55199             index = record;
55200             record = ds.getAt(index);
55201         }else{
55202             index = ds.indexOf(record);
55203         }
55204         this.insertRows(ds, index, index, true);
55205         this.onRemove(ds, record, index+1, true);
55206         this.syncRowHeights(index, index);
55207         this.layout();
55208         this.fireEvent("rowupdated", this, index, record);
55209     },
55210
55211     onAdd : function(ds, records, index){
55212         this.insertRows(ds, index, index + (records.length-1));
55213     },
55214
55215     onRemove : function(ds, record, index, isUpdate){
55216         if(isUpdate !== true){
55217             this.fireEvent("beforerowremoved", this, index, record);
55218         }
55219         var bt = this.getBodyTable(), lt = this.getLockedTable();
55220         if(bt.rows[index]){
55221             bt.firstChild.removeChild(bt.rows[index]);
55222         }
55223         if(lt.rows[index]){
55224             lt.firstChild.removeChild(lt.rows[index]);
55225         }
55226         if(isUpdate !== true){
55227             this.stripeRows(index);
55228             this.syncRowHeights(index, index);
55229             this.layout();
55230             this.fireEvent("rowremoved", this, index, record);
55231         }
55232     },
55233
55234     onLoad : function(){
55235         this.scrollToTop();
55236     },
55237
55238     /**
55239      * Scrolls the grid to the top
55240      */
55241     scrollToTop : function(){
55242         if(this.scroller){
55243             this.scroller.dom.scrollTop = 0;
55244             this.syncScroll();
55245         }
55246     },
55247
55248     /**
55249      * Gets a panel in the header of the grid that can be used for toolbars etc.
55250      * After modifying the contents of this panel a call to grid.autoSize() may be
55251      * required to register any changes in size.
55252      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55253      * @return Roo.Element
55254      */
55255     getHeaderPanel : function(doShow){
55256         if(doShow){
55257             this.headerPanel.show();
55258         }
55259         return this.headerPanel;
55260     },
55261
55262     /**
55263      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55264      * After modifying the contents of this panel a call to grid.autoSize() may be
55265      * required to register any changes in size.
55266      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55267      * @return Roo.Element
55268      */
55269     getFooterPanel : function(doShow){
55270         if(doShow){
55271             this.footerPanel.show();
55272         }
55273         return this.footerPanel;
55274     },
55275
55276     initElements : function(){
55277         var E = Roo.Element;
55278         var el = this.grid.getGridEl().dom.firstChild;
55279         var cs = el.childNodes;
55280
55281         this.el = new E(el);
55282         
55283          this.focusEl = new E(el.firstChild);
55284         this.focusEl.swallowEvent("click", true);
55285         
55286         this.headerPanel = new E(cs[1]);
55287         this.headerPanel.enableDisplayMode("block");
55288
55289         this.scroller = new E(cs[2]);
55290         this.scrollSizer = new E(this.scroller.dom.firstChild);
55291
55292         this.lockedWrap = new E(cs[3]);
55293         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55294         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55295
55296         this.mainWrap = new E(cs[4]);
55297         this.mainHd = new E(this.mainWrap.dom.firstChild);
55298         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55299
55300         this.footerPanel = new E(cs[5]);
55301         this.footerPanel.enableDisplayMode("block");
55302
55303         this.resizeProxy = new E(cs[6]);
55304
55305         this.headerSelector = String.format(
55306            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55307            this.lockedHd.id, this.mainHd.id
55308         );
55309
55310         this.splitterSelector = String.format(
55311            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55312            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55313         );
55314     },
55315     idToCssName : function(s)
55316     {
55317         return s.replace(/[^a-z0-9]+/ig, '-');
55318     },
55319
55320     getHeaderCell : function(index){
55321         return Roo.DomQuery.select(this.headerSelector)[index];
55322     },
55323
55324     getHeaderCellMeasure : function(index){
55325         return this.getHeaderCell(index).firstChild;
55326     },
55327
55328     getHeaderCellText : function(index){
55329         return this.getHeaderCell(index).firstChild.firstChild;
55330     },
55331
55332     getLockedTable : function(){
55333         return this.lockedBody.dom.firstChild;
55334     },
55335
55336     getBodyTable : function(){
55337         return this.mainBody.dom.firstChild;
55338     },
55339
55340     getLockedRow : function(index){
55341         return this.getLockedTable().rows[index];
55342     },
55343
55344     getRow : function(index){
55345         return this.getBodyTable().rows[index];
55346     },
55347
55348     getRowComposite : function(index){
55349         if(!this.rowEl){
55350             this.rowEl = new Roo.CompositeElementLite();
55351         }
55352         var els = [], lrow, mrow;
55353         if(lrow = this.getLockedRow(index)){
55354             els.push(lrow);
55355         }
55356         if(mrow = this.getRow(index)){
55357             els.push(mrow);
55358         }
55359         this.rowEl.elements = els;
55360         return this.rowEl;
55361     },
55362     /**
55363      * Gets the 'td' of the cell
55364      * 
55365      * @param {Integer} rowIndex row to select
55366      * @param {Integer} colIndex column to select
55367      * 
55368      * @return {Object} 
55369      */
55370     getCell : function(rowIndex, colIndex){
55371         var locked = this.cm.getLockedCount();
55372         var source;
55373         if(colIndex < locked){
55374             source = this.lockedBody.dom.firstChild;
55375         }else{
55376             source = this.mainBody.dom.firstChild;
55377             colIndex -= locked;
55378         }
55379         return source.rows[rowIndex].childNodes[colIndex];
55380     },
55381
55382     getCellText : function(rowIndex, colIndex){
55383         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55384     },
55385
55386     getCellBox : function(cell){
55387         var b = this.fly(cell).getBox();
55388         if(Roo.isOpera){ // opera fails to report the Y
55389             b.y = cell.offsetTop + this.mainBody.getY();
55390         }
55391         return b;
55392     },
55393
55394     getCellIndex : function(cell){
55395         var id = String(cell.className).match(this.cellRE);
55396         if(id){
55397             return parseInt(id[1], 10);
55398         }
55399         return 0;
55400     },
55401
55402     findHeaderIndex : function(n){
55403         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55404         return r ? this.getCellIndex(r) : false;
55405     },
55406
55407     findHeaderCell : function(n){
55408         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55409         return r ? r : false;
55410     },
55411
55412     findRowIndex : function(n){
55413         if(!n){
55414             return false;
55415         }
55416         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55417         return r ? r.rowIndex : false;
55418     },
55419
55420     findCellIndex : function(node){
55421         var stop = this.el.dom;
55422         while(node && node != stop){
55423             if(this.findRE.test(node.className)){
55424                 return this.getCellIndex(node);
55425             }
55426             node = node.parentNode;
55427         }
55428         return false;
55429     },
55430
55431     getColumnId : function(index){
55432         return this.cm.getColumnId(index);
55433     },
55434
55435     getSplitters : function()
55436     {
55437         if(this.splitterSelector){
55438            return Roo.DomQuery.select(this.splitterSelector);
55439         }else{
55440             return null;
55441       }
55442     },
55443
55444     getSplitter : function(index){
55445         return this.getSplitters()[index];
55446     },
55447
55448     onRowOver : function(e, t){
55449         var row;
55450         if((row = this.findRowIndex(t)) !== false){
55451             this.getRowComposite(row).addClass("x-grid-row-over");
55452         }
55453     },
55454
55455     onRowOut : function(e, t){
55456         var row;
55457         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55458             this.getRowComposite(row).removeClass("x-grid-row-over");
55459         }
55460     },
55461
55462     renderHeaders : function(){
55463         var cm = this.cm;
55464         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55465         var cb = [], lb = [], sb = [], lsb = [], p = {};
55466         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55467             p.cellId = "x-grid-hd-0-" + i;
55468             p.splitId = "x-grid-csplit-0-" + i;
55469             p.id = cm.getColumnId(i);
55470             p.value = cm.getColumnHeader(i) || "";
55471             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55472             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55473             if(!cm.isLocked(i)){
55474                 cb[cb.length] = ct.apply(p);
55475                 sb[sb.length] = st.apply(p);
55476             }else{
55477                 lb[lb.length] = ct.apply(p);
55478                 lsb[lsb.length] = st.apply(p);
55479             }
55480         }
55481         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55482                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55483     },
55484
55485     updateHeaders : function(){
55486         var html = this.renderHeaders();
55487         this.lockedHd.update(html[0]);
55488         this.mainHd.update(html[1]);
55489     },
55490
55491     /**
55492      * Focuses the specified row.
55493      * @param {Number} row The row index
55494      */
55495     focusRow : function(row)
55496     {
55497         //Roo.log('GridView.focusRow');
55498         var x = this.scroller.dom.scrollLeft;
55499         this.focusCell(row, 0, false);
55500         this.scroller.dom.scrollLeft = x;
55501     },
55502
55503     /**
55504      * Focuses the specified cell.
55505      * @param {Number} row The row index
55506      * @param {Number} col The column index
55507      * @param {Boolean} hscroll false to disable horizontal scrolling
55508      */
55509     focusCell : function(row, col, hscroll)
55510     {
55511         //Roo.log('GridView.focusCell');
55512         var el = this.ensureVisible(row, col, hscroll);
55513         this.focusEl.alignTo(el, "tl-tl");
55514         if(Roo.isGecko){
55515             this.focusEl.focus();
55516         }else{
55517             this.focusEl.focus.defer(1, this.focusEl);
55518         }
55519     },
55520
55521     /**
55522      * Scrolls the specified cell into view
55523      * @param {Number} row The row index
55524      * @param {Number} col The column index
55525      * @param {Boolean} hscroll false to disable horizontal scrolling
55526      */
55527     ensureVisible : function(row, col, hscroll)
55528     {
55529         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55530         //return null; //disable for testing.
55531         if(typeof row != "number"){
55532             row = row.rowIndex;
55533         }
55534         if(row < 0 && row >= this.ds.getCount()){
55535             return  null;
55536         }
55537         col = (col !== undefined ? col : 0);
55538         var cm = this.grid.colModel;
55539         while(cm.isHidden(col)){
55540             col++;
55541         }
55542
55543         var el = this.getCell(row, col);
55544         if(!el){
55545             return null;
55546         }
55547         var c = this.scroller.dom;
55548
55549         var ctop = parseInt(el.offsetTop, 10);
55550         var cleft = parseInt(el.offsetLeft, 10);
55551         var cbot = ctop + el.offsetHeight;
55552         var cright = cleft + el.offsetWidth;
55553         
55554         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55555         var stop = parseInt(c.scrollTop, 10);
55556         var sleft = parseInt(c.scrollLeft, 10);
55557         var sbot = stop + ch;
55558         var sright = sleft + c.clientWidth;
55559         /*
55560         Roo.log('GridView.ensureVisible:' +
55561                 ' ctop:' + ctop +
55562                 ' c.clientHeight:' + c.clientHeight +
55563                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55564                 ' stop:' + stop +
55565                 ' cbot:' + cbot +
55566                 ' sbot:' + sbot +
55567                 ' ch:' + ch  
55568                 );
55569         */
55570         if(ctop < stop){
55571              c.scrollTop = ctop;
55572             //Roo.log("set scrolltop to ctop DISABLE?");
55573         }else if(cbot > sbot){
55574             //Roo.log("set scrolltop to cbot-ch");
55575             c.scrollTop = cbot-ch;
55576         }
55577         
55578         if(hscroll !== false){
55579             if(cleft < sleft){
55580                 c.scrollLeft = cleft;
55581             }else if(cright > sright){
55582                 c.scrollLeft = cright-c.clientWidth;
55583             }
55584         }
55585          
55586         return el;
55587     },
55588
55589     updateColumns : function(){
55590         this.grid.stopEditing();
55591         var cm = this.grid.colModel, colIds = this.getColumnIds();
55592         //var totalWidth = cm.getTotalWidth();
55593         var pos = 0;
55594         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55595             //if(cm.isHidden(i)) continue;
55596             var w = cm.getColumnWidth(i);
55597             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55598             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55599         }
55600         this.updateSplitters();
55601     },
55602
55603     generateRules : function(cm){
55604         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55605         Roo.util.CSS.removeStyleSheet(rulesId);
55606         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55607             var cid = cm.getColumnId(i);
55608             var align = '';
55609             if(cm.config[i].align){
55610                 align = 'text-align:'+cm.config[i].align+';';
55611             }
55612             var hidden = '';
55613             if(cm.isHidden(i)){
55614                 hidden = 'display:none;';
55615             }
55616             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55617             ruleBuf.push(
55618                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55619                     this.hdSelector, cid, " {\n", align, width, "}\n",
55620                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55621                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55622         }
55623         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55624     },
55625
55626     updateSplitters : function(){
55627         var cm = this.cm, s = this.getSplitters();
55628         if(s){ // splitters not created yet
55629             var pos = 0, locked = true;
55630             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55631                 if(cm.isHidden(i)) {
55632                     continue;
55633                 }
55634                 var w = cm.getColumnWidth(i); // make sure it's a number
55635                 if(!cm.isLocked(i) && locked){
55636                     pos = 0;
55637                     locked = false;
55638                 }
55639                 pos += w;
55640                 s[i].style.left = (pos-this.splitOffset) + "px";
55641             }
55642         }
55643     },
55644
55645     handleHiddenChange : function(colModel, colIndex, hidden){
55646         if(hidden){
55647             this.hideColumn(colIndex);
55648         }else{
55649             this.unhideColumn(colIndex);
55650         }
55651     },
55652
55653     hideColumn : function(colIndex){
55654         var cid = this.getColumnId(colIndex);
55655         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55656         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55657         if(Roo.isSafari){
55658             this.updateHeaders();
55659         }
55660         this.updateSplitters();
55661         this.layout();
55662     },
55663
55664     unhideColumn : function(colIndex){
55665         var cid = this.getColumnId(colIndex);
55666         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55667         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55668
55669         if(Roo.isSafari){
55670             this.updateHeaders();
55671         }
55672         this.updateSplitters();
55673         this.layout();
55674     },
55675
55676     insertRows : function(dm, firstRow, lastRow, isUpdate){
55677         if(firstRow == 0 && lastRow == dm.getCount()-1){
55678             this.refresh();
55679         }else{
55680             if(!isUpdate){
55681                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55682             }
55683             var s = this.getScrollState();
55684             var markup = this.renderRows(firstRow, lastRow);
55685             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55686             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55687             this.restoreScroll(s);
55688             if(!isUpdate){
55689                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55690                 this.syncRowHeights(firstRow, lastRow);
55691                 this.stripeRows(firstRow);
55692                 this.layout();
55693             }
55694         }
55695     },
55696
55697     bufferRows : function(markup, target, index){
55698         var before = null, trows = target.rows, tbody = target.tBodies[0];
55699         if(index < trows.length){
55700             before = trows[index];
55701         }
55702         var b = document.createElement("div");
55703         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55704         var rows = b.firstChild.rows;
55705         for(var i = 0, len = rows.length; i < len; i++){
55706             if(before){
55707                 tbody.insertBefore(rows[0], before);
55708             }else{
55709                 tbody.appendChild(rows[0]);
55710             }
55711         }
55712         b.innerHTML = "";
55713         b = null;
55714     },
55715
55716     deleteRows : function(dm, firstRow, lastRow){
55717         if(dm.getRowCount()<1){
55718             this.fireEvent("beforerefresh", this);
55719             this.mainBody.update("");
55720             this.lockedBody.update("");
55721             this.fireEvent("refresh", this);
55722         }else{
55723             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55724             var bt = this.getBodyTable();
55725             var tbody = bt.firstChild;
55726             var rows = bt.rows;
55727             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55728                 tbody.removeChild(rows[firstRow]);
55729             }
55730             this.stripeRows(firstRow);
55731             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55732         }
55733     },
55734
55735     updateRows : function(dataSource, firstRow, lastRow){
55736         var s = this.getScrollState();
55737         this.refresh();
55738         this.restoreScroll(s);
55739     },
55740
55741     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55742         if(!noRefresh){
55743            this.refresh();
55744         }
55745         this.updateHeaderSortState();
55746     },
55747
55748     getScrollState : function(){
55749         
55750         var sb = this.scroller.dom;
55751         return {left: sb.scrollLeft, top: sb.scrollTop};
55752     },
55753
55754     stripeRows : function(startRow){
55755         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55756             return;
55757         }
55758         startRow = startRow || 0;
55759         var rows = this.getBodyTable().rows;
55760         var lrows = this.getLockedTable().rows;
55761         var cls = ' x-grid-row-alt ';
55762         for(var i = startRow, len = rows.length; i < len; i++){
55763             var row = rows[i], lrow = lrows[i];
55764             var isAlt = ((i+1) % 2 == 0);
55765             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55766             if(isAlt == hasAlt){
55767                 continue;
55768             }
55769             if(isAlt){
55770                 row.className += " x-grid-row-alt";
55771             }else{
55772                 row.className = row.className.replace("x-grid-row-alt", "");
55773             }
55774             if(lrow){
55775                 lrow.className = row.className;
55776             }
55777         }
55778     },
55779
55780     restoreScroll : function(state){
55781         //Roo.log('GridView.restoreScroll');
55782         var sb = this.scroller.dom;
55783         sb.scrollLeft = state.left;
55784         sb.scrollTop = state.top;
55785         this.syncScroll();
55786     },
55787
55788     syncScroll : function(){
55789         //Roo.log('GridView.syncScroll');
55790         var sb = this.scroller.dom;
55791         var sh = this.mainHd.dom;
55792         var bs = this.mainBody.dom;
55793         var lv = this.lockedBody.dom;
55794         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55795         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55796     },
55797
55798     handleScroll : function(e){
55799         this.syncScroll();
55800         var sb = this.scroller.dom;
55801         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55802         e.stopEvent();
55803     },
55804
55805     handleWheel : function(e){
55806         var d = e.getWheelDelta();
55807         this.scroller.dom.scrollTop -= d*22;
55808         // set this here to prevent jumpy scrolling on large tables
55809         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55810         e.stopEvent();
55811     },
55812
55813     renderRows : function(startRow, endRow){
55814         // pull in all the crap needed to render rows
55815         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55816         var colCount = cm.getColumnCount();
55817
55818         if(ds.getCount() < 1){
55819             return ["", ""];
55820         }
55821
55822         // build a map for all the columns
55823         var cs = [];
55824         for(var i = 0; i < colCount; i++){
55825             var name = cm.getDataIndex(i);
55826             cs[i] = {
55827                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55828                 renderer : cm.getRenderer(i),
55829                 id : cm.getColumnId(i),
55830                 locked : cm.isLocked(i),
55831                 has_editor : cm.isCellEditable(i)
55832             };
55833         }
55834
55835         startRow = startRow || 0;
55836         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55837
55838         // records to render
55839         var rs = ds.getRange(startRow, endRow);
55840
55841         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55842     },
55843
55844     // As much as I hate to duplicate code, this was branched because FireFox really hates
55845     // [].join("") on strings. The performance difference was substantial enough to
55846     // branch this function
55847     doRender : Roo.isGecko ?
55848             function(cs, rs, ds, startRow, colCount, stripe){
55849                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55850                 // buffers
55851                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55852                 
55853                 var hasListener = this.grid.hasListener('rowclass');
55854                 var rowcfg = {};
55855                 for(var j = 0, len = rs.length; j < len; j++){
55856                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55857                     for(var i = 0; i < colCount; i++){
55858                         c = cs[i];
55859                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55860                         p.id = c.id;
55861                         p.css = p.attr = "";
55862                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55863                         if(p.value == undefined || p.value === "") {
55864                             p.value = "&#160;";
55865                         }
55866                         if(c.has_editor){
55867                             p.css += ' x-grid-editable-cell';
55868                         }
55869                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55870                             p.css +=  ' x-grid-dirty-cell';
55871                         }
55872                         var markup = ct.apply(p);
55873                         if(!c.locked){
55874                             cb+= markup;
55875                         }else{
55876                             lcb+= markup;
55877                         }
55878                     }
55879                     var alt = [];
55880                     if(stripe && ((rowIndex+1) % 2 == 0)){
55881                         alt.push("x-grid-row-alt")
55882                     }
55883                     if(r.dirty){
55884                         alt.push(  " x-grid-dirty-row");
55885                     }
55886                     rp.cells = lcb;
55887                     if(this.getRowClass){
55888                         alt.push(this.getRowClass(r, rowIndex));
55889                     }
55890                     if (hasListener) {
55891                         rowcfg = {
55892                              
55893                             record: r,
55894                             rowIndex : rowIndex,
55895                             rowClass : ''
55896                         };
55897                         this.grid.fireEvent('rowclass', this, rowcfg);
55898                         alt.push(rowcfg.rowClass);
55899                     }
55900                     rp.alt = alt.join(" ");
55901                     lbuf+= rt.apply(rp);
55902                     rp.cells = cb;
55903                     buf+=  rt.apply(rp);
55904                 }
55905                 return [lbuf, buf];
55906             } :
55907             function(cs, rs, ds, startRow, colCount, stripe){
55908                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55909                 // buffers
55910                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55911                 var hasListener = this.grid.hasListener('rowclass');
55912  
55913                 var rowcfg = {};
55914                 for(var j = 0, len = rs.length; j < len; j++){
55915                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55916                     for(var i = 0; i < colCount; i++){
55917                         c = cs[i];
55918                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55919                         p.id = c.id;
55920                         p.css = p.attr = "";
55921                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55922                         if(p.value == undefined || p.value === "") {
55923                             p.value = "&#160;";
55924                         }
55925                         //Roo.log(c);
55926                          if(c.has_editor){
55927                             p.css += ' x-grid-editable-cell';
55928                         }
55929                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55930                             p.css += ' x-grid-dirty-cell' 
55931                         }
55932                         
55933                         var markup = ct.apply(p);
55934                         if(!c.locked){
55935                             cb[cb.length] = markup;
55936                         }else{
55937                             lcb[lcb.length] = markup;
55938                         }
55939                     }
55940                     var alt = [];
55941                     if(stripe && ((rowIndex+1) % 2 == 0)){
55942                         alt.push( "x-grid-row-alt");
55943                     }
55944                     if(r.dirty){
55945                         alt.push(" x-grid-dirty-row");
55946                     }
55947                     rp.cells = lcb;
55948                     if(this.getRowClass){
55949                         alt.push( this.getRowClass(r, rowIndex));
55950                     }
55951                     if (hasListener) {
55952                         rowcfg = {
55953                              
55954                             record: r,
55955                             rowIndex : rowIndex,
55956                             rowClass : ''
55957                         };
55958                         this.grid.fireEvent('rowclass', this, rowcfg);
55959                         alt.push(rowcfg.rowClass);
55960                     }
55961                     
55962                     rp.alt = alt.join(" ");
55963                     rp.cells = lcb.join("");
55964                     lbuf[lbuf.length] = rt.apply(rp);
55965                     rp.cells = cb.join("");
55966                     buf[buf.length] =  rt.apply(rp);
55967                 }
55968                 return [lbuf.join(""), buf.join("")];
55969             },
55970
55971     renderBody : function(){
55972         var markup = this.renderRows();
55973         var bt = this.templates.body;
55974         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
55975     },
55976
55977     /**
55978      * Refreshes the grid
55979      * @param {Boolean} headersToo
55980      */
55981     refresh : function(headersToo){
55982         this.fireEvent("beforerefresh", this);
55983         this.grid.stopEditing();
55984         var result = this.renderBody();
55985         this.lockedBody.update(result[0]);
55986         this.mainBody.update(result[1]);
55987         if(headersToo === true){
55988             this.updateHeaders();
55989             this.updateColumns();
55990             this.updateSplitters();
55991             this.updateHeaderSortState();
55992         }
55993         this.syncRowHeights();
55994         this.layout();
55995         this.fireEvent("refresh", this);
55996     },
55997
55998     handleColumnMove : function(cm, oldIndex, newIndex){
55999         this.indexMap = null;
56000         var s = this.getScrollState();
56001         this.refresh(true);
56002         this.restoreScroll(s);
56003         this.afterMove(newIndex);
56004     },
56005
56006     afterMove : function(colIndex){
56007         if(this.enableMoveAnim && Roo.enableFx){
56008             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56009         }
56010         // if multisort - fix sortOrder, and reload..
56011         if (this.grid.dataSource.multiSort) {
56012             // the we can call sort again..
56013             var dm = this.grid.dataSource;
56014             var cm = this.grid.colModel;
56015             var so = [];
56016             for(var i = 0; i < cm.config.length; i++ ) {
56017                 
56018                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56019                     continue; // dont' bother, it's not in sort list or being set.
56020                 }
56021                 
56022                 so.push(cm.config[i].dataIndex);
56023             };
56024             dm.sortOrder = so;
56025             dm.load(dm.lastOptions);
56026             
56027             
56028         }
56029         
56030     },
56031
56032     updateCell : function(dm, rowIndex, dataIndex){
56033         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56034         if(typeof colIndex == "undefined"){ // not present in grid
56035             return;
56036         }
56037         var cm = this.grid.colModel;
56038         var cell = this.getCell(rowIndex, colIndex);
56039         var cellText = this.getCellText(rowIndex, colIndex);
56040
56041         var p = {
56042             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56043             id : cm.getColumnId(colIndex),
56044             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56045         };
56046         var renderer = cm.getRenderer(colIndex);
56047         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56048         if(typeof val == "undefined" || val === "") {
56049             val = "&#160;";
56050         }
56051         cellText.innerHTML = val;
56052         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56053         this.syncRowHeights(rowIndex, rowIndex);
56054     },
56055
56056     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56057         var maxWidth = 0;
56058         if(this.grid.autoSizeHeaders){
56059             var h = this.getHeaderCellMeasure(colIndex);
56060             maxWidth = Math.max(maxWidth, h.scrollWidth);
56061         }
56062         var tb, index;
56063         if(this.cm.isLocked(colIndex)){
56064             tb = this.getLockedTable();
56065             index = colIndex;
56066         }else{
56067             tb = this.getBodyTable();
56068             index = colIndex - this.cm.getLockedCount();
56069         }
56070         if(tb && tb.rows){
56071             var rows = tb.rows;
56072             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56073             for(var i = 0; i < stopIndex; i++){
56074                 var cell = rows[i].childNodes[index].firstChild;
56075                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56076             }
56077         }
56078         return maxWidth + /*margin for error in IE*/ 5;
56079     },
56080     /**
56081      * Autofit a column to its content.
56082      * @param {Number} colIndex
56083      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56084      */
56085      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56086          if(this.cm.isHidden(colIndex)){
56087              return; // can't calc a hidden column
56088          }
56089         if(forceMinSize){
56090             var cid = this.cm.getColumnId(colIndex);
56091             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56092            if(this.grid.autoSizeHeaders){
56093                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56094            }
56095         }
56096         var newWidth = this.calcColumnWidth(colIndex);
56097         this.cm.setColumnWidth(colIndex,
56098             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56099         if(!suppressEvent){
56100             this.grid.fireEvent("columnresize", colIndex, newWidth);
56101         }
56102     },
56103
56104     /**
56105      * Autofits all columns to their content and then expands to fit any extra space in the grid
56106      */
56107      autoSizeColumns : function(){
56108         var cm = this.grid.colModel;
56109         var colCount = cm.getColumnCount();
56110         for(var i = 0; i < colCount; i++){
56111             this.autoSizeColumn(i, true, true);
56112         }
56113         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56114             this.fitColumns();
56115         }else{
56116             this.updateColumns();
56117             this.layout();
56118         }
56119     },
56120
56121     /**
56122      * Autofits all columns to the grid's width proportionate with their current size
56123      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56124      */
56125     fitColumns : function(reserveScrollSpace){
56126         var cm = this.grid.colModel;
56127         var colCount = cm.getColumnCount();
56128         var cols = [];
56129         var width = 0;
56130         var i, w;
56131         for (i = 0; i < colCount; i++){
56132             if(!cm.isHidden(i) && !cm.isFixed(i)){
56133                 w = cm.getColumnWidth(i);
56134                 cols.push(i);
56135                 cols.push(w);
56136                 width += w;
56137             }
56138         }
56139         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56140         if(reserveScrollSpace){
56141             avail -= 17;
56142         }
56143         var frac = (avail - cm.getTotalWidth())/width;
56144         while (cols.length){
56145             w = cols.pop();
56146             i = cols.pop();
56147             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56148         }
56149         this.updateColumns();
56150         this.layout();
56151     },
56152
56153     onRowSelect : function(rowIndex){
56154         var row = this.getRowComposite(rowIndex);
56155         row.addClass("x-grid-row-selected");
56156     },
56157
56158     onRowDeselect : function(rowIndex){
56159         var row = this.getRowComposite(rowIndex);
56160         row.removeClass("x-grid-row-selected");
56161     },
56162
56163     onCellSelect : function(row, col){
56164         var cell = this.getCell(row, col);
56165         if(cell){
56166             Roo.fly(cell).addClass("x-grid-cell-selected");
56167         }
56168     },
56169
56170     onCellDeselect : function(row, col){
56171         var cell = this.getCell(row, col);
56172         if(cell){
56173             Roo.fly(cell).removeClass("x-grid-cell-selected");
56174         }
56175     },
56176
56177     updateHeaderSortState : function(){
56178         
56179         // sort state can be single { field: xxx, direction : yyy}
56180         // or   { xxx=>ASC , yyy : DESC ..... }
56181         
56182         var mstate = {};
56183         if (!this.ds.multiSort) { 
56184             var state = this.ds.getSortState();
56185             if(!state){
56186                 return;
56187             }
56188             mstate[state.field] = state.direction;
56189             // FIXME... - this is not used here.. but might be elsewhere..
56190             this.sortState = state;
56191             
56192         } else {
56193             mstate = this.ds.sortToggle;
56194         }
56195         //remove existing sort classes..
56196         
56197         var sc = this.sortClasses;
56198         var hds = this.el.select(this.headerSelector).removeClass(sc);
56199         
56200         for(var f in mstate) {
56201         
56202             var sortColumn = this.cm.findColumnIndex(f);
56203             
56204             if(sortColumn != -1){
56205                 var sortDir = mstate[f];        
56206                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56207             }
56208         }
56209         
56210          
56211         
56212     },
56213
56214
56215     handleHeaderClick : function(g, index,e){
56216         
56217         Roo.log("header click");
56218         
56219         if (Roo.isTouch) {
56220             // touch events on header are handled by context
56221             this.handleHdCtx(g,index,e);
56222             return;
56223         }
56224         
56225         
56226         if(this.headersDisabled){
56227             return;
56228         }
56229         var dm = g.dataSource, cm = g.colModel;
56230         if(!cm.isSortable(index)){
56231             return;
56232         }
56233         g.stopEditing();
56234         
56235         if (dm.multiSort) {
56236             // update the sortOrder
56237             var so = [];
56238             for(var i = 0; i < cm.config.length; i++ ) {
56239                 
56240                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56241                     continue; // dont' bother, it's not in sort list or being set.
56242                 }
56243                 
56244                 so.push(cm.config[i].dataIndex);
56245             };
56246             dm.sortOrder = so;
56247         }
56248         
56249         
56250         dm.sort(cm.getDataIndex(index));
56251     },
56252
56253
56254     destroy : function(){
56255         if(this.colMenu){
56256             this.colMenu.removeAll();
56257             Roo.menu.MenuMgr.unregister(this.colMenu);
56258             this.colMenu.getEl().remove();
56259             delete this.colMenu;
56260         }
56261         if(this.hmenu){
56262             this.hmenu.removeAll();
56263             Roo.menu.MenuMgr.unregister(this.hmenu);
56264             this.hmenu.getEl().remove();
56265             delete this.hmenu;
56266         }
56267         if(this.grid.enableColumnMove){
56268             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56269             if(dds){
56270                 for(var dd in dds){
56271                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56272                         var elid = dds[dd].dragElId;
56273                         dds[dd].unreg();
56274                         Roo.get(elid).remove();
56275                     } else if(dds[dd].config.isTarget){
56276                         dds[dd].proxyTop.remove();
56277                         dds[dd].proxyBottom.remove();
56278                         dds[dd].unreg();
56279                     }
56280                     if(Roo.dd.DDM.locationCache[dd]){
56281                         delete Roo.dd.DDM.locationCache[dd];
56282                     }
56283                 }
56284                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56285             }
56286         }
56287         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56288         this.bind(null, null);
56289         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56290     },
56291
56292     handleLockChange : function(){
56293         this.refresh(true);
56294     },
56295
56296     onDenyColumnLock : function(){
56297
56298     },
56299
56300     onDenyColumnHide : function(){
56301
56302     },
56303
56304     handleHdMenuClick : function(item){
56305         var index = this.hdCtxIndex;
56306         var cm = this.cm, ds = this.ds;
56307         switch(item.id){
56308             case "asc":
56309                 ds.sort(cm.getDataIndex(index), "ASC");
56310                 break;
56311             case "desc":
56312                 ds.sort(cm.getDataIndex(index), "DESC");
56313                 break;
56314             case "lock":
56315                 var lc = cm.getLockedCount();
56316                 if(cm.getColumnCount(true) <= lc+1){
56317                     this.onDenyColumnLock();
56318                     return;
56319                 }
56320                 if(lc != index){
56321                     cm.setLocked(index, true, true);
56322                     cm.moveColumn(index, lc);
56323                     this.grid.fireEvent("columnmove", index, lc);
56324                 }else{
56325                     cm.setLocked(index, true);
56326                 }
56327             break;
56328             case "unlock":
56329                 var lc = cm.getLockedCount();
56330                 if((lc-1) != index){
56331                     cm.setLocked(index, false, true);
56332                     cm.moveColumn(index, lc-1);
56333                     this.grid.fireEvent("columnmove", index, lc-1);
56334                 }else{
56335                     cm.setLocked(index, false);
56336                 }
56337             break;
56338             case 'wider': // used to expand cols on touch..
56339             case 'narrow':
56340                 var cw = cm.getColumnWidth(index);
56341                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56342                 cw = Math.max(0, cw);
56343                 cw = Math.min(cw,4000);
56344                 cm.setColumnWidth(index, cw);
56345                 break;
56346                 
56347             default:
56348                 index = cm.getIndexById(item.id.substr(4));
56349                 if(index != -1){
56350                     if(item.checked && cm.getColumnCount(true) <= 1){
56351                         this.onDenyColumnHide();
56352                         return false;
56353                     }
56354                     cm.setHidden(index, item.checked);
56355                 }
56356         }
56357         return true;
56358     },
56359
56360     beforeColMenuShow : function(){
56361         var cm = this.cm,  colCount = cm.getColumnCount();
56362         this.colMenu.removeAll();
56363         for(var i = 0; i < colCount; i++){
56364             this.colMenu.add(new Roo.menu.CheckItem({
56365                 id: "col-"+cm.getColumnId(i),
56366                 text: cm.getColumnHeader(i),
56367                 checked: !cm.isHidden(i),
56368                 hideOnClick:false
56369             }));
56370         }
56371     },
56372
56373     handleHdCtx : function(g, index, e){
56374         e.stopEvent();
56375         var hd = this.getHeaderCell(index);
56376         this.hdCtxIndex = index;
56377         var ms = this.hmenu.items, cm = this.cm;
56378         ms.get("asc").setDisabled(!cm.isSortable(index));
56379         ms.get("desc").setDisabled(!cm.isSortable(index));
56380         if(this.grid.enableColLock !== false){
56381             ms.get("lock").setDisabled(cm.isLocked(index));
56382             ms.get("unlock").setDisabled(!cm.isLocked(index));
56383         }
56384         this.hmenu.show(hd, "tl-bl");
56385     },
56386
56387     handleHdOver : function(e){
56388         var hd = this.findHeaderCell(e.getTarget());
56389         if(hd && !this.headersDisabled){
56390             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56391                this.fly(hd).addClass("x-grid-hd-over");
56392             }
56393         }
56394     },
56395
56396     handleHdOut : function(e){
56397         var hd = this.findHeaderCell(e.getTarget());
56398         if(hd){
56399             this.fly(hd).removeClass("x-grid-hd-over");
56400         }
56401     },
56402
56403     handleSplitDblClick : function(e, t){
56404         var i = this.getCellIndex(t);
56405         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56406             this.autoSizeColumn(i, true);
56407             this.layout();
56408         }
56409     },
56410
56411     render : function(){
56412
56413         var cm = this.cm;
56414         var colCount = cm.getColumnCount();
56415
56416         if(this.grid.monitorWindowResize === true){
56417             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56418         }
56419         var header = this.renderHeaders();
56420         var body = this.templates.body.apply({rows:""});
56421         var html = this.templates.master.apply({
56422             lockedBody: body,
56423             body: body,
56424             lockedHeader: header[0],
56425             header: header[1]
56426         });
56427
56428         //this.updateColumns();
56429
56430         this.grid.getGridEl().dom.innerHTML = html;
56431
56432         this.initElements();
56433         
56434         // a kludge to fix the random scolling effect in webkit
56435         this.el.on("scroll", function() {
56436             this.el.dom.scrollTop=0; // hopefully not recursive..
56437         },this);
56438
56439         this.scroller.on("scroll", this.handleScroll, this);
56440         this.lockedBody.on("mousewheel", this.handleWheel, this);
56441         this.mainBody.on("mousewheel", this.handleWheel, this);
56442
56443         this.mainHd.on("mouseover", this.handleHdOver, this);
56444         this.mainHd.on("mouseout", this.handleHdOut, this);
56445         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56446                 {delegate: "."+this.splitClass});
56447
56448         this.lockedHd.on("mouseover", this.handleHdOver, this);
56449         this.lockedHd.on("mouseout", this.handleHdOut, this);
56450         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56451                 {delegate: "."+this.splitClass});
56452
56453         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56454             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56455         }
56456
56457         this.updateSplitters();
56458
56459         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56460             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56461             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56462         }
56463
56464         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56465             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56466             this.hmenu.add(
56467                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56468                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56469             );
56470             if(this.grid.enableColLock !== false){
56471                 this.hmenu.add('-',
56472                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56473                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56474                 );
56475             }
56476             if (Roo.isTouch) {
56477                  this.hmenu.add('-',
56478                     {id:"wider", text: this.columnsWiderText},
56479                     {id:"narrow", text: this.columnsNarrowText }
56480                 );
56481                 
56482                  
56483             }
56484             
56485             if(this.grid.enableColumnHide !== false){
56486
56487                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56488                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56489                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56490
56491                 this.hmenu.add('-',
56492                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56493                 );
56494             }
56495             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56496
56497             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56498         }
56499
56500         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56501             this.dd = new Roo.grid.GridDragZone(this.grid, {
56502                 ddGroup : this.grid.ddGroup || 'GridDD'
56503             });
56504             
56505         }
56506
56507         /*
56508         for(var i = 0; i < colCount; i++){
56509             if(cm.isHidden(i)){
56510                 this.hideColumn(i);
56511             }
56512             if(cm.config[i].align){
56513                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56514                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56515             }
56516         }*/
56517         
56518         this.updateHeaderSortState();
56519
56520         this.beforeInitialResize();
56521         this.layout(true);
56522
56523         // two part rendering gives faster view to the user
56524         this.renderPhase2.defer(1, this);
56525     },
56526
56527     renderPhase2 : function(){
56528         // render the rows now
56529         this.refresh();
56530         if(this.grid.autoSizeColumns){
56531             this.autoSizeColumns();
56532         }
56533     },
56534
56535     beforeInitialResize : function(){
56536
56537     },
56538
56539     onColumnSplitterMoved : function(i, w){
56540         this.userResized = true;
56541         var cm = this.grid.colModel;
56542         cm.setColumnWidth(i, w, true);
56543         var cid = cm.getColumnId(i);
56544         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56545         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56546         this.updateSplitters();
56547         this.layout();
56548         this.grid.fireEvent("columnresize", i, w);
56549     },
56550
56551     syncRowHeights : function(startIndex, endIndex){
56552         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56553             startIndex = startIndex || 0;
56554             var mrows = this.getBodyTable().rows;
56555             var lrows = this.getLockedTable().rows;
56556             var len = mrows.length-1;
56557             endIndex = Math.min(endIndex || len, len);
56558             for(var i = startIndex; i <= endIndex; i++){
56559                 var m = mrows[i], l = lrows[i];
56560                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56561                 m.style.height = l.style.height = h + "px";
56562             }
56563         }
56564     },
56565
56566     layout : function(initialRender, is2ndPass){
56567         var g = this.grid;
56568         var auto = g.autoHeight;
56569         var scrollOffset = 16;
56570         var c = g.getGridEl(), cm = this.cm,
56571                 expandCol = g.autoExpandColumn,
56572                 gv = this;
56573         //c.beginMeasure();
56574
56575         if(!c.dom.offsetWidth){ // display:none?
56576             if(initialRender){
56577                 this.lockedWrap.show();
56578                 this.mainWrap.show();
56579             }
56580             return;
56581         }
56582
56583         var hasLock = this.cm.isLocked(0);
56584
56585         var tbh = this.headerPanel.getHeight();
56586         var bbh = this.footerPanel.getHeight();
56587
56588         if(auto){
56589             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56590             var newHeight = ch + c.getBorderWidth("tb");
56591             if(g.maxHeight){
56592                 newHeight = Math.min(g.maxHeight, newHeight);
56593             }
56594             c.setHeight(newHeight);
56595         }
56596
56597         if(g.autoWidth){
56598             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56599         }
56600
56601         var s = this.scroller;
56602
56603         var csize = c.getSize(true);
56604
56605         this.el.setSize(csize.width, csize.height);
56606
56607         this.headerPanel.setWidth(csize.width);
56608         this.footerPanel.setWidth(csize.width);
56609
56610         var hdHeight = this.mainHd.getHeight();
56611         var vw = csize.width;
56612         var vh = csize.height - (tbh + bbh);
56613
56614         s.setSize(vw, vh);
56615
56616         var bt = this.getBodyTable();
56617         
56618         if(cm.getLockedCount() == cm.config.length){
56619             bt = this.getLockedTable();
56620         }
56621         
56622         var ltWidth = hasLock ?
56623                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56624
56625         var scrollHeight = bt.offsetHeight;
56626         var scrollWidth = ltWidth + bt.offsetWidth;
56627         var vscroll = false, hscroll = false;
56628
56629         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56630
56631         var lw = this.lockedWrap, mw = this.mainWrap;
56632         var lb = this.lockedBody, mb = this.mainBody;
56633
56634         setTimeout(function(){
56635             var t = s.dom.offsetTop;
56636             var w = s.dom.clientWidth,
56637                 h = s.dom.clientHeight;
56638
56639             lw.setTop(t);
56640             lw.setSize(ltWidth, h);
56641
56642             mw.setLeftTop(ltWidth, t);
56643             mw.setSize(w-ltWidth, h);
56644
56645             lb.setHeight(h-hdHeight);
56646             mb.setHeight(h-hdHeight);
56647
56648             if(is2ndPass !== true && !gv.userResized && expandCol){
56649                 // high speed resize without full column calculation
56650                 
56651                 var ci = cm.getIndexById(expandCol);
56652                 if (ci < 0) {
56653                     ci = cm.findColumnIndex(expandCol);
56654                 }
56655                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56656                 var expandId = cm.getColumnId(ci);
56657                 var  tw = cm.getTotalWidth(false);
56658                 var currentWidth = cm.getColumnWidth(ci);
56659                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56660                 if(currentWidth != cw){
56661                     cm.setColumnWidth(ci, cw, true);
56662                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56663                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56664                     gv.updateSplitters();
56665                     gv.layout(false, true);
56666                 }
56667             }
56668
56669             if(initialRender){
56670                 lw.show();
56671                 mw.show();
56672             }
56673             //c.endMeasure();
56674         }, 10);
56675     },
56676
56677     onWindowResize : function(){
56678         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56679             return;
56680         }
56681         this.layout();
56682     },
56683
56684     appendFooter : function(parentEl){
56685         return null;
56686     },
56687
56688     sortAscText : "Sort Ascending",
56689     sortDescText : "Sort Descending",
56690     lockText : "Lock Column",
56691     unlockText : "Unlock Column",
56692     columnsText : "Columns",
56693  
56694     columnsWiderText : "Wider",
56695     columnsNarrowText : "Thinner"
56696 });
56697
56698
56699 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56700     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56701     this.proxy.el.addClass('x-grid3-col-dd');
56702 };
56703
56704 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56705     handleMouseDown : function(e){
56706
56707     },
56708
56709     callHandleMouseDown : function(e){
56710         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56711     }
56712 });
56713 /*
56714  * Based on:
56715  * Ext JS Library 1.1.1
56716  * Copyright(c) 2006-2007, Ext JS, LLC.
56717  *
56718  * Originally Released Under LGPL - original licence link has changed is not relivant.
56719  *
56720  * Fork - LGPL
56721  * <script type="text/javascript">
56722  */
56723  
56724 // private
56725 // This is a support class used internally by the Grid components
56726 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56727     this.grid = grid;
56728     this.view = grid.getView();
56729     this.proxy = this.view.resizeProxy;
56730     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56731         "gridSplitters" + this.grid.getGridEl().id, {
56732         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56733     });
56734     this.setHandleElId(Roo.id(hd));
56735     this.setOuterHandleElId(Roo.id(hd2));
56736     this.scroll = false;
56737 };
56738 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56739     fly: Roo.Element.fly,
56740
56741     b4StartDrag : function(x, y){
56742         this.view.headersDisabled = true;
56743         this.proxy.setHeight(this.view.mainWrap.getHeight());
56744         var w = this.cm.getColumnWidth(this.cellIndex);
56745         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56746         this.resetConstraints();
56747         this.setXConstraint(minw, 1000);
56748         this.setYConstraint(0, 0);
56749         this.minX = x - minw;
56750         this.maxX = x + 1000;
56751         this.startPos = x;
56752         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56753     },
56754
56755
56756     handleMouseDown : function(e){
56757         ev = Roo.EventObject.setEvent(e);
56758         var t = this.fly(ev.getTarget());
56759         if(t.hasClass("x-grid-split")){
56760             this.cellIndex = this.view.getCellIndex(t.dom);
56761             this.split = t.dom;
56762             this.cm = this.grid.colModel;
56763             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56764                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56765             }
56766         }
56767     },
56768
56769     endDrag : function(e){
56770         this.view.headersDisabled = false;
56771         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56772         var diff = endX - this.startPos;
56773         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56774     },
56775
56776     autoOffset : function(){
56777         this.setDelta(0,0);
56778     }
56779 });/*
56780  * Based on:
56781  * Ext JS Library 1.1.1
56782  * Copyright(c) 2006-2007, Ext JS, LLC.
56783  *
56784  * Originally Released Under LGPL - original licence link has changed is not relivant.
56785  *
56786  * Fork - LGPL
56787  * <script type="text/javascript">
56788  */
56789  
56790 // private
56791 // This is a support class used internally by the Grid components
56792 Roo.grid.GridDragZone = function(grid, config){
56793     this.view = grid.getView();
56794     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56795     if(this.view.lockedBody){
56796         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56797         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56798     }
56799     this.scroll = false;
56800     this.grid = grid;
56801     this.ddel = document.createElement('div');
56802     this.ddel.className = 'x-grid-dd-wrap';
56803 };
56804
56805 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56806     ddGroup : "GridDD",
56807
56808     getDragData : function(e){
56809         var t = Roo.lib.Event.getTarget(e);
56810         var rowIndex = this.view.findRowIndex(t);
56811         var sm = this.grid.selModel;
56812             
56813         //Roo.log(rowIndex);
56814         
56815         if (sm.getSelectedCell) {
56816             // cell selection..
56817             if (!sm.getSelectedCell()) {
56818                 return false;
56819             }
56820             if (rowIndex != sm.getSelectedCell()[0]) {
56821                 return false;
56822             }
56823         
56824         }
56825         
56826         if(rowIndex !== false){
56827             
56828             // if editorgrid.. 
56829             
56830             
56831             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56832                
56833             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56834               //  
56835             //}
56836             if (e.hasModifier()){
56837                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56838             }
56839             
56840             Roo.log("getDragData");
56841             
56842             return {
56843                 grid: this.grid,
56844                 ddel: this.ddel,
56845                 rowIndex: rowIndex,
56846                 selections:sm.getSelections ? sm.getSelections() : (
56847                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56848                 )
56849             };
56850         }
56851         return false;
56852     },
56853
56854     onInitDrag : function(e){
56855         var data = this.dragData;
56856         this.ddel.innerHTML = this.grid.getDragDropText();
56857         this.proxy.update(this.ddel);
56858         // fire start drag?
56859     },
56860
56861     afterRepair : function(){
56862         this.dragging = false;
56863     },
56864
56865     getRepairXY : function(e, data){
56866         return false;
56867     },
56868
56869     onEndDrag : function(data, e){
56870         // fire end drag?
56871     },
56872
56873     onValidDrop : function(dd, e, id){
56874         // fire drag drop?
56875         this.hideProxy();
56876     },
56877
56878     beforeInvalidDrop : function(e, id){
56879
56880     }
56881 });/*
56882  * Based on:
56883  * Ext JS Library 1.1.1
56884  * Copyright(c) 2006-2007, Ext JS, LLC.
56885  *
56886  * Originally Released Under LGPL - original licence link has changed is not relivant.
56887  *
56888  * Fork - LGPL
56889  * <script type="text/javascript">
56890  */
56891  
56892
56893 /**
56894  * @class Roo.grid.ColumnModel
56895  * @extends Roo.util.Observable
56896  * This is the default implementation of a ColumnModel used by the Grid. It defines
56897  * the columns in the grid.
56898  * <br>Usage:<br>
56899  <pre><code>
56900  var colModel = new Roo.grid.ColumnModel([
56901         {header: "Ticker", width: 60, sortable: true, locked: true},
56902         {header: "Company Name", width: 150, sortable: true},
56903         {header: "Market Cap.", width: 100, sortable: true},
56904         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56905         {header: "Employees", width: 100, sortable: true, resizable: false}
56906  ]);
56907  </code></pre>
56908  * <p>
56909  
56910  * The config options listed for this class are options which may appear in each
56911  * individual column definition.
56912  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56913  * @constructor
56914  * @param {Object} config An Array of column config objects. See this class's
56915  * config objects for details.
56916 */
56917 Roo.grid.ColumnModel = function(config){
56918         /**
56919      * The config passed into the constructor
56920      */
56921     this.config = config;
56922     this.lookup = {};
56923
56924     // if no id, create one
56925     // if the column does not have a dataIndex mapping,
56926     // map it to the order it is in the config
56927     for(var i = 0, len = config.length; i < len; i++){
56928         var c = config[i];
56929         if(typeof c.dataIndex == "undefined"){
56930             c.dataIndex = i;
56931         }
56932         if(typeof c.renderer == "string"){
56933             c.renderer = Roo.util.Format[c.renderer];
56934         }
56935         if(typeof c.id == "undefined"){
56936             c.id = Roo.id();
56937         }
56938         if(c.editor && c.editor.xtype){
56939             c.editor  = Roo.factory(c.editor, Roo.grid);
56940         }
56941         if(c.editor && c.editor.isFormField){
56942             c.editor = new Roo.grid.GridEditor(c.editor);
56943         }
56944         this.lookup[c.id] = c;
56945     }
56946
56947     /**
56948      * The width of columns which have no width specified (defaults to 100)
56949      * @type Number
56950      */
56951     this.defaultWidth = 100;
56952
56953     /**
56954      * Default sortable of columns which have no sortable specified (defaults to false)
56955      * @type Boolean
56956      */
56957     this.defaultSortable = false;
56958
56959     this.addEvents({
56960         /**
56961              * @event widthchange
56962              * Fires when the width of a column changes.
56963              * @param {ColumnModel} this
56964              * @param {Number} columnIndex The column index
56965              * @param {Number} newWidth The new width
56966              */
56967             "widthchange": true,
56968         /**
56969              * @event headerchange
56970              * Fires when the text of a header changes.
56971              * @param {ColumnModel} this
56972              * @param {Number} columnIndex The column index
56973              * @param {Number} newText The new header text
56974              */
56975             "headerchange": true,
56976         /**
56977              * @event hiddenchange
56978              * Fires when a column is hidden or "unhidden".
56979              * @param {ColumnModel} this
56980              * @param {Number} columnIndex The column index
56981              * @param {Boolean} hidden true if hidden, false otherwise
56982              */
56983             "hiddenchange": true,
56984             /**
56985          * @event columnmoved
56986          * Fires when a column is moved.
56987          * @param {ColumnModel} this
56988          * @param {Number} oldIndex
56989          * @param {Number} newIndex
56990          */
56991         "columnmoved" : true,
56992         /**
56993          * @event columlockchange
56994          * Fires when a column's locked state is changed
56995          * @param {ColumnModel} this
56996          * @param {Number} colIndex
56997          * @param {Boolean} locked true if locked
56998          */
56999         "columnlockchange" : true
57000     });
57001     Roo.grid.ColumnModel.superclass.constructor.call(this);
57002 };
57003 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57004     /**
57005      * @cfg {String} header The header text to display in the Grid view.
57006      */
57007     /**
57008      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57009      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57010      * specified, the column's index is used as an index into the Record's data Array.
57011      */
57012     /**
57013      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57014      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57015      */
57016     /**
57017      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57018      * Defaults to the value of the {@link #defaultSortable} property.
57019      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57020      */
57021     /**
57022      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57023      */
57024     /**
57025      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57026      */
57027     /**
57028      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57029      */
57030     /**
57031      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57032      */
57033     /**
57034      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57035      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57036      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57037      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57038      */
57039        /**
57040      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57041      */
57042     /**
57043      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57044      */
57045     /**
57046      * @cfg {String} cursor (Optional)
57047      */
57048     /**
57049      * @cfg {String} tooltip (Optional)
57050      */
57051     /**
57052      * @cfg {Number} xs (Optional)
57053      */
57054     /**
57055      * @cfg {Number} sm (Optional)
57056      */
57057     /**
57058      * @cfg {Number} md (Optional)
57059      */
57060     /**
57061      * @cfg {Number} lg (Optional)
57062      */
57063     /**
57064      * Returns the id of the column at the specified index.
57065      * @param {Number} index The column index
57066      * @return {String} the id
57067      */
57068     getColumnId : function(index){
57069         return this.config[index].id;
57070     },
57071
57072     /**
57073      * Returns the column for a specified id.
57074      * @param {String} id The column id
57075      * @return {Object} the column
57076      */
57077     getColumnById : function(id){
57078         return this.lookup[id];
57079     },
57080
57081     
57082     /**
57083      * Returns the column for a specified dataIndex.
57084      * @param {String} dataIndex The column dataIndex
57085      * @return {Object|Boolean} the column or false if not found
57086      */
57087     getColumnByDataIndex: function(dataIndex){
57088         var index = this.findColumnIndex(dataIndex);
57089         return index > -1 ? this.config[index] : false;
57090     },
57091     
57092     /**
57093      * Returns the index for a specified column id.
57094      * @param {String} id The column id
57095      * @return {Number} the index, or -1 if not found
57096      */
57097     getIndexById : function(id){
57098         for(var i = 0, len = this.config.length; i < len; i++){
57099             if(this.config[i].id == id){
57100                 return i;
57101             }
57102         }
57103         return -1;
57104     },
57105     
57106     /**
57107      * Returns the index for a specified column dataIndex.
57108      * @param {String} dataIndex The column dataIndex
57109      * @return {Number} the index, or -1 if not found
57110      */
57111     
57112     findColumnIndex : function(dataIndex){
57113         for(var i = 0, len = this.config.length; i < len; i++){
57114             if(this.config[i].dataIndex == dataIndex){
57115                 return i;
57116             }
57117         }
57118         return -1;
57119     },
57120     
57121     
57122     moveColumn : function(oldIndex, newIndex){
57123         var c = this.config[oldIndex];
57124         this.config.splice(oldIndex, 1);
57125         this.config.splice(newIndex, 0, c);
57126         this.dataMap = null;
57127         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57128     },
57129
57130     isLocked : function(colIndex){
57131         return this.config[colIndex].locked === true;
57132     },
57133
57134     setLocked : function(colIndex, value, suppressEvent){
57135         if(this.isLocked(colIndex) == value){
57136             return;
57137         }
57138         this.config[colIndex].locked = value;
57139         if(!suppressEvent){
57140             this.fireEvent("columnlockchange", this, colIndex, value);
57141         }
57142     },
57143
57144     getTotalLockedWidth : function(){
57145         var totalWidth = 0;
57146         for(var i = 0; i < this.config.length; i++){
57147             if(this.isLocked(i) && !this.isHidden(i)){
57148                 this.totalWidth += this.getColumnWidth(i);
57149             }
57150         }
57151         return totalWidth;
57152     },
57153
57154     getLockedCount : function(){
57155         for(var i = 0, len = this.config.length; i < len; i++){
57156             if(!this.isLocked(i)){
57157                 return i;
57158             }
57159         }
57160         
57161         return this.config.length;
57162     },
57163
57164     /**
57165      * Returns the number of columns.
57166      * @return {Number}
57167      */
57168     getColumnCount : function(visibleOnly){
57169         if(visibleOnly === true){
57170             var c = 0;
57171             for(var i = 0, len = this.config.length; i < len; i++){
57172                 if(!this.isHidden(i)){
57173                     c++;
57174                 }
57175             }
57176             return c;
57177         }
57178         return this.config.length;
57179     },
57180
57181     /**
57182      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57183      * @param {Function} fn
57184      * @param {Object} scope (optional)
57185      * @return {Array} result
57186      */
57187     getColumnsBy : function(fn, scope){
57188         var r = [];
57189         for(var i = 0, len = this.config.length; i < len; i++){
57190             var c = this.config[i];
57191             if(fn.call(scope||this, c, i) === true){
57192                 r[r.length] = c;
57193             }
57194         }
57195         return r;
57196     },
57197
57198     /**
57199      * Returns true if the specified column is sortable.
57200      * @param {Number} col The column index
57201      * @return {Boolean}
57202      */
57203     isSortable : function(col){
57204         if(typeof this.config[col].sortable == "undefined"){
57205             return this.defaultSortable;
57206         }
57207         return this.config[col].sortable;
57208     },
57209
57210     /**
57211      * Returns the rendering (formatting) function defined for the column.
57212      * @param {Number} col The column index.
57213      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57214      */
57215     getRenderer : function(col){
57216         if(!this.config[col].renderer){
57217             return Roo.grid.ColumnModel.defaultRenderer;
57218         }
57219         return this.config[col].renderer;
57220     },
57221
57222     /**
57223      * Sets the rendering (formatting) function for a column.
57224      * @param {Number} col The column index
57225      * @param {Function} fn The function to use to process the cell's raw data
57226      * to return HTML markup for the grid view. The render function is called with
57227      * the following parameters:<ul>
57228      * <li>Data value.</li>
57229      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57230      * <li>css A CSS style string to apply to the table cell.</li>
57231      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57232      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57233      * <li>Row index</li>
57234      * <li>Column index</li>
57235      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57236      */
57237     setRenderer : function(col, fn){
57238         this.config[col].renderer = fn;
57239     },
57240
57241     /**
57242      * Returns the width for the specified column.
57243      * @param {Number} col The column index
57244      * @return {Number}
57245      */
57246     getColumnWidth : function(col){
57247         return this.config[col].width * 1 || this.defaultWidth;
57248     },
57249
57250     /**
57251      * Sets the width for a column.
57252      * @param {Number} col The column index
57253      * @param {Number} width The new width
57254      */
57255     setColumnWidth : function(col, width, suppressEvent){
57256         this.config[col].width = width;
57257         this.totalWidth = null;
57258         if(!suppressEvent){
57259              this.fireEvent("widthchange", this, col, width);
57260         }
57261     },
57262
57263     /**
57264      * Returns the total width of all columns.
57265      * @param {Boolean} includeHidden True to include hidden column widths
57266      * @return {Number}
57267      */
57268     getTotalWidth : function(includeHidden){
57269         if(!this.totalWidth){
57270             this.totalWidth = 0;
57271             for(var i = 0, len = this.config.length; i < len; i++){
57272                 if(includeHidden || !this.isHidden(i)){
57273                     this.totalWidth += this.getColumnWidth(i);
57274                 }
57275             }
57276         }
57277         return this.totalWidth;
57278     },
57279
57280     /**
57281      * Returns the header for the specified column.
57282      * @param {Number} col The column index
57283      * @return {String}
57284      */
57285     getColumnHeader : function(col){
57286         return this.config[col].header;
57287     },
57288
57289     /**
57290      * Sets the header for a column.
57291      * @param {Number} col The column index
57292      * @param {String} header The new header
57293      */
57294     setColumnHeader : function(col, header){
57295         this.config[col].header = header;
57296         this.fireEvent("headerchange", this, col, header);
57297     },
57298
57299     /**
57300      * Returns the tooltip for the specified column.
57301      * @param {Number} col The column index
57302      * @return {String}
57303      */
57304     getColumnTooltip : function(col){
57305             return this.config[col].tooltip;
57306     },
57307     /**
57308      * Sets the tooltip for a column.
57309      * @param {Number} col The column index
57310      * @param {String} tooltip The new tooltip
57311      */
57312     setColumnTooltip : function(col, tooltip){
57313             this.config[col].tooltip = tooltip;
57314     },
57315
57316     /**
57317      * Returns the dataIndex for the specified column.
57318      * @param {Number} col The column index
57319      * @return {Number}
57320      */
57321     getDataIndex : function(col){
57322         return this.config[col].dataIndex;
57323     },
57324
57325     /**
57326      * Sets the dataIndex for a column.
57327      * @param {Number} col The column index
57328      * @param {Number} dataIndex The new dataIndex
57329      */
57330     setDataIndex : function(col, dataIndex){
57331         this.config[col].dataIndex = dataIndex;
57332     },
57333
57334     
57335     
57336     /**
57337      * Returns true if the cell is editable.
57338      * @param {Number} colIndex The column index
57339      * @param {Number} rowIndex The row index - this is nto actually used..?
57340      * @return {Boolean}
57341      */
57342     isCellEditable : function(colIndex, rowIndex){
57343         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57344     },
57345
57346     /**
57347      * Returns the editor defined for the cell/column.
57348      * return false or null to disable editing.
57349      * @param {Number} colIndex The column index
57350      * @param {Number} rowIndex The row index
57351      * @return {Object}
57352      */
57353     getCellEditor : function(colIndex, rowIndex){
57354         return this.config[colIndex].editor;
57355     },
57356
57357     /**
57358      * Sets if a column is editable.
57359      * @param {Number} col The column index
57360      * @param {Boolean} editable True if the column is editable
57361      */
57362     setEditable : function(col, editable){
57363         this.config[col].editable = editable;
57364     },
57365
57366
57367     /**
57368      * Returns true if the column is hidden.
57369      * @param {Number} colIndex The column index
57370      * @return {Boolean}
57371      */
57372     isHidden : function(colIndex){
57373         return this.config[colIndex].hidden;
57374     },
57375
57376
57377     /**
57378      * Returns true if the column width cannot be changed
57379      */
57380     isFixed : function(colIndex){
57381         return this.config[colIndex].fixed;
57382     },
57383
57384     /**
57385      * Returns true if the column can be resized
57386      * @return {Boolean}
57387      */
57388     isResizable : function(colIndex){
57389         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57390     },
57391     /**
57392      * Sets if a column is hidden.
57393      * @param {Number} colIndex The column index
57394      * @param {Boolean} hidden True if the column is hidden
57395      */
57396     setHidden : function(colIndex, hidden){
57397         this.config[colIndex].hidden = hidden;
57398         this.totalWidth = null;
57399         this.fireEvent("hiddenchange", this, colIndex, hidden);
57400     },
57401
57402     /**
57403      * Sets the editor for a column.
57404      * @param {Number} col The column index
57405      * @param {Object} editor The editor object
57406      */
57407     setEditor : function(col, editor){
57408         this.config[col].editor = editor;
57409     }
57410 });
57411
57412 Roo.grid.ColumnModel.defaultRenderer = function(value)
57413 {
57414     if(typeof value == "object") {
57415         return value;
57416     }
57417         if(typeof value == "string" && value.length < 1){
57418             return "&#160;";
57419         }
57420     
57421         return String.format("{0}", value);
57422 };
57423
57424 // Alias for backwards compatibility
57425 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57426 /*
57427  * Based on:
57428  * Ext JS Library 1.1.1
57429  * Copyright(c) 2006-2007, Ext JS, LLC.
57430  *
57431  * Originally Released Under LGPL - original licence link has changed is not relivant.
57432  *
57433  * Fork - LGPL
57434  * <script type="text/javascript">
57435  */
57436
57437 /**
57438  * @class Roo.grid.AbstractSelectionModel
57439  * @extends Roo.util.Observable
57440  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57441  * implemented by descendant classes.  This class should not be directly instantiated.
57442  * @constructor
57443  */
57444 Roo.grid.AbstractSelectionModel = function(){
57445     this.locked = false;
57446     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57447 };
57448
57449 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57450     /** @ignore Called by the grid automatically. Do not call directly. */
57451     init : function(grid){
57452         this.grid = grid;
57453         this.initEvents();
57454     },
57455
57456     /**
57457      * Locks the selections.
57458      */
57459     lock : function(){
57460         this.locked = true;
57461     },
57462
57463     /**
57464      * Unlocks the selections.
57465      */
57466     unlock : function(){
57467         this.locked = false;
57468     },
57469
57470     /**
57471      * Returns true if the selections are locked.
57472      * @return {Boolean}
57473      */
57474     isLocked : function(){
57475         return this.locked;
57476     }
57477 });/*
57478  * Based on:
57479  * Ext JS Library 1.1.1
57480  * Copyright(c) 2006-2007, Ext JS, LLC.
57481  *
57482  * Originally Released Under LGPL - original licence link has changed is not relivant.
57483  *
57484  * Fork - LGPL
57485  * <script type="text/javascript">
57486  */
57487 /**
57488  * @extends Roo.grid.AbstractSelectionModel
57489  * @class Roo.grid.RowSelectionModel
57490  * The default SelectionModel used by {@link Roo.grid.Grid}.
57491  * It supports multiple selections and keyboard selection/navigation. 
57492  * @constructor
57493  * @param {Object} config
57494  */
57495 Roo.grid.RowSelectionModel = function(config){
57496     Roo.apply(this, config);
57497     this.selections = new Roo.util.MixedCollection(false, function(o){
57498         return o.id;
57499     });
57500
57501     this.last = false;
57502     this.lastActive = false;
57503
57504     this.addEvents({
57505         /**
57506              * @event selectionchange
57507              * Fires when the selection changes
57508              * @param {SelectionModel} this
57509              */
57510             "selectionchange" : true,
57511         /**
57512              * @event afterselectionchange
57513              * Fires after the selection changes (eg. by key press or clicking)
57514              * @param {SelectionModel} this
57515              */
57516             "afterselectionchange" : true,
57517         /**
57518              * @event beforerowselect
57519              * Fires when a row is selected being selected, return false to cancel.
57520              * @param {SelectionModel} this
57521              * @param {Number} rowIndex The selected index
57522              * @param {Boolean} keepExisting False if other selections will be cleared
57523              */
57524             "beforerowselect" : true,
57525         /**
57526              * @event rowselect
57527              * Fires when a row is selected.
57528              * @param {SelectionModel} this
57529              * @param {Number} rowIndex The selected index
57530              * @param {Roo.data.Record} r The record
57531              */
57532             "rowselect" : true,
57533         /**
57534              * @event rowdeselect
57535              * Fires when a row is deselected.
57536              * @param {SelectionModel} this
57537              * @param {Number} rowIndex The selected index
57538              */
57539         "rowdeselect" : true
57540     });
57541     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57542     this.locked = false;
57543 };
57544
57545 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57546     /**
57547      * @cfg {Boolean} singleSelect
57548      * True to allow selection of only one row at a time (defaults to false)
57549      */
57550     singleSelect : false,
57551
57552     // private
57553     initEvents : function(){
57554
57555         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57556             this.grid.on("mousedown", this.handleMouseDown, this);
57557         }else{ // allow click to work like normal
57558             this.grid.on("rowclick", this.handleDragableRowClick, this);
57559         }
57560
57561         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57562             "up" : function(e){
57563                 if(!e.shiftKey){
57564                     this.selectPrevious(e.shiftKey);
57565                 }else if(this.last !== false && this.lastActive !== false){
57566                     var last = this.last;
57567                     this.selectRange(this.last,  this.lastActive-1);
57568                     this.grid.getView().focusRow(this.lastActive);
57569                     if(last !== false){
57570                         this.last = last;
57571                     }
57572                 }else{
57573                     this.selectFirstRow();
57574                 }
57575                 this.fireEvent("afterselectionchange", this);
57576             },
57577             "down" : function(e){
57578                 if(!e.shiftKey){
57579                     this.selectNext(e.shiftKey);
57580                 }else if(this.last !== false && this.lastActive !== false){
57581                     var last = this.last;
57582                     this.selectRange(this.last,  this.lastActive+1);
57583                     this.grid.getView().focusRow(this.lastActive);
57584                     if(last !== false){
57585                         this.last = last;
57586                     }
57587                 }else{
57588                     this.selectFirstRow();
57589                 }
57590                 this.fireEvent("afterselectionchange", this);
57591             },
57592             scope: this
57593         });
57594
57595         var view = this.grid.view;
57596         view.on("refresh", this.onRefresh, this);
57597         view.on("rowupdated", this.onRowUpdated, this);
57598         view.on("rowremoved", this.onRemove, this);
57599     },
57600
57601     // private
57602     onRefresh : function(){
57603         var ds = this.grid.dataSource, i, v = this.grid.view;
57604         var s = this.selections;
57605         s.each(function(r){
57606             if((i = ds.indexOfId(r.id)) != -1){
57607                 v.onRowSelect(i);
57608                 s.add(ds.getAt(i)); // updating the selection relate data
57609             }else{
57610                 s.remove(r);
57611             }
57612         });
57613     },
57614
57615     // private
57616     onRemove : function(v, index, r){
57617         this.selections.remove(r);
57618     },
57619
57620     // private
57621     onRowUpdated : function(v, index, r){
57622         if(this.isSelected(r)){
57623             v.onRowSelect(index);
57624         }
57625     },
57626
57627     /**
57628      * Select records.
57629      * @param {Array} records The records to select
57630      * @param {Boolean} keepExisting (optional) True to keep existing selections
57631      */
57632     selectRecords : function(records, keepExisting){
57633         if(!keepExisting){
57634             this.clearSelections();
57635         }
57636         var ds = this.grid.dataSource;
57637         for(var i = 0, len = records.length; i < len; i++){
57638             this.selectRow(ds.indexOf(records[i]), true);
57639         }
57640     },
57641
57642     /**
57643      * Gets the number of selected rows.
57644      * @return {Number}
57645      */
57646     getCount : function(){
57647         return this.selections.length;
57648     },
57649
57650     /**
57651      * Selects the first row in the grid.
57652      */
57653     selectFirstRow : function(){
57654         this.selectRow(0);
57655     },
57656
57657     /**
57658      * Select the last row.
57659      * @param {Boolean} keepExisting (optional) True to keep existing selections
57660      */
57661     selectLastRow : function(keepExisting){
57662         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57663     },
57664
57665     /**
57666      * Selects the row immediately following the last selected row.
57667      * @param {Boolean} keepExisting (optional) True to keep existing selections
57668      */
57669     selectNext : function(keepExisting){
57670         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57671             this.selectRow(this.last+1, keepExisting);
57672             this.grid.getView().focusRow(this.last);
57673         }
57674     },
57675
57676     /**
57677      * Selects the row that precedes the last selected row.
57678      * @param {Boolean} keepExisting (optional) True to keep existing selections
57679      */
57680     selectPrevious : function(keepExisting){
57681         if(this.last){
57682             this.selectRow(this.last-1, keepExisting);
57683             this.grid.getView().focusRow(this.last);
57684         }
57685     },
57686
57687     /**
57688      * Returns the selected records
57689      * @return {Array} Array of selected records
57690      */
57691     getSelections : function(){
57692         return [].concat(this.selections.items);
57693     },
57694
57695     /**
57696      * Returns the first selected record.
57697      * @return {Record}
57698      */
57699     getSelected : function(){
57700         return this.selections.itemAt(0);
57701     },
57702
57703
57704     /**
57705      * Clears all selections.
57706      */
57707     clearSelections : function(fast){
57708         if(this.locked) {
57709             return;
57710         }
57711         if(fast !== true){
57712             var ds = this.grid.dataSource;
57713             var s = this.selections;
57714             s.each(function(r){
57715                 this.deselectRow(ds.indexOfId(r.id));
57716             }, this);
57717             s.clear();
57718         }else{
57719             this.selections.clear();
57720         }
57721         this.last = false;
57722     },
57723
57724
57725     /**
57726      * Selects all rows.
57727      */
57728     selectAll : function(){
57729         if(this.locked) {
57730             return;
57731         }
57732         this.selections.clear();
57733         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57734             this.selectRow(i, true);
57735         }
57736     },
57737
57738     /**
57739      * Returns True if there is a selection.
57740      * @return {Boolean}
57741      */
57742     hasSelection : function(){
57743         return this.selections.length > 0;
57744     },
57745
57746     /**
57747      * Returns True if the specified row is selected.
57748      * @param {Number/Record} record The record or index of the record to check
57749      * @return {Boolean}
57750      */
57751     isSelected : function(index){
57752         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57753         return (r && this.selections.key(r.id) ? true : false);
57754     },
57755
57756     /**
57757      * Returns True if the specified record id is selected.
57758      * @param {String} id The id of record to check
57759      * @return {Boolean}
57760      */
57761     isIdSelected : function(id){
57762         return (this.selections.key(id) ? true : false);
57763     },
57764
57765     // private
57766     handleMouseDown : function(e, t){
57767         var view = this.grid.getView(), rowIndex;
57768         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57769             return;
57770         };
57771         if(e.shiftKey && this.last !== false){
57772             var last = this.last;
57773             this.selectRange(last, rowIndex, e.ctrlKey);
57774             this.last = last; // reset the last
57775             view.focusRow(rowIndex);
57776         }else{
57777             var isSelected = this.isSelected(rowIndex);
57778             if(e.button !== 0 && isSelected){
57779                 view.focusRow(rowIndex);
57780             }else if(e.ctrlKey && isSelected){
57781                 this.deselectRow(rowIndex);
57782             }else if(!isSelected){
57783                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57784                 view.focusRow(rowIndex);
57785             }
57786         }
57787         this.fireEvent("afterselectionchange", this);
57788     },
57789     // private
57790     handleDragableRowClick :  function(grid, rowIndex, e) 
57791     {
57792         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57793             this.selectRow(rowIndex, false);
57794             grid.view.focusRow(rowIndex);
57795              this.fireEvent("afterselectionchange", this);
57796         }
57797     },
57798     
57799     /**
57800      * Selects multiple rows.
57801      * @param {Array} rows Array of the indexes of the row to select
57802      * @param {Boolean} keepExisting (optional) True to keep existing selections
57803      */
57804     selectRows : function(rows, keepExisting){
57805         if(!keepExisting){
57806             this.clearSelections();
57807         }
57808         for(var i = 0, len = rows.length; i < len; i++){
57809             this.selectRow(rows[i], true);
57810         }
57811     },
57812
57813     /**
57814      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57815      * @param {Number} startRow The index of the first row in the range
57816      * @param {Number} endRow The index of the last row in the range
57817      * @param {Boolean} keepExisting (optional) True to retain existing selections
57818      */
57819     selectRange : function(startRow, endRow, keepExisting){
57820         if(this.locked) {
57821             return;
57822         }
57823         if(!keepExisting){
57824             this.clearSelections();
57825         }
57826         if(startRow <= endRow){
57827             for(var i = startRow; i <= endRow; i++){
57828                 this.selectRow(i, true);
57829             }
57830         }else{
57831             for(var i = startRow; i >= endRow; i--){
57832                 this.selectRow(i, true);
57833             }
57834         }
57835     },
57836
57837     /**
57838      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57839      * @param {Number} startRow The index of the first row in the range
57840      * @param {Number} endRow The index of the last row in the range
57841      */
57842     deselectRange : function(startRow, endRow, preventViewNotify){
57843         if(this.locked) {
57844             return;
57845         }
57846         for(var i = startRow; i <= endRow; i++){
57847             this.deselectRow(i, preventViewNotify);
57848         }
57849     },
57850
57851     /**
57852      * Selects a row.
57853      * @param {Number} row The index of the row to select
57854      * @param {Boolean} keepExisting (optional) True to keep existing selections
57855      */
57856     selectRow : function(index, keepExisting, preventViewNotify){
57857         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57858             return;
57859         }
57860         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57861             if(!keepExisting || this.singleSelect){
57862                 this.clearSelections();
57863             }
57864             var r = this.grid.dataSource.getAt(index);
57865             this.selections.add(r);
57866             this.last = this.lastActive = index;
57867             if(!preventViewNotify){
57868                 this.grid.getView().onRowSelect(index);
57869             }
57870             this.fireEvent("rowselect", this, index, r);
57871             this.fireEvent("selectionchange", this);
57872         }
57873     },
57874
57875     /**
57876      * Deselects a row.
57877      * @param {Number} row The index of the row to deselect
57878      */
57879     deselectRow : function(index, preventViewNotify){
57880         if(this.locked) {
57881             return;
57882         }
57883         if(this.last == index){
57884             this.last = false;
57885         }
57886         if(this.lastActive == index){
57887             this.lastActive = false;
57888         }
57889         var r = this.grid.dataSource.getAt(index);
57890         this.selections.remove(r);
57891         if(!preventViewNotify){
57892             this.grid.getView().onRowDeselect(index);
57893         }
57894         this.fireEvent("rowdeselect", this, index);
57895         this.fireEvent("selectionchange", this);
57896     },
57897
57898     // private
57899     restoreLast : function(){
57900         if(this._last){
57901             this.last = this._last;
57902         }
57903     },
57904
57905     // private
57906     acceptsNav : function(row, col, cm){
57907         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57908     },
57909
57910     // private
57911     onEditorKey : function(field, e){
57912         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57913         if(k == e.TAB){
57914             e.stopEvent();
57915             ed.completeEdit();
57916             if(e.shiftKey){
57917                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57918             }else{
57919                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57920             }
57921         }else if(k == e.ENTER && !e.ctrlKey){
57922             e.stopEvent();
57923             ed.completeEdit();
57924             if(e.shiftKey){
57925                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57926             }else{
57927                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57928             }
57929         }else if(k == e.ESC){
57930             ed.cancelEdit();
57931         }
57932         if(newCell){
57933             g.startEditing(newCell[0], newCell[1]);
57934         }
57935     }
57936 });/*
57937  * Based on:
57938  * Ext JS Library 1.1.1
57939  * Copyright(c) 2006-2007, Ext JS, LLC.
57940  *
57941  * Originally Released Under LGPL - original licence link has changed is not relivant.
57942  *
57943  * Fork - LGPL
57944  * <script type="text/javascript">
57945  */
57946 /**
57947  * @class Roo.grid.CellSelectionModel
57948  * @extends Roo.grid.AbstractSelectionModel
57949  * This class provides the basic implementation for cell selection in a grid.
57950  * @constructor
57951  * @param {Object} config The object containing the configuration of this model.
57952  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57953  */
57954 Roo.grid.CellSelectionModel = function(config){
57955     Roo.apply(this, config);
57956
57957     this.selection = null;
57958
57959     this.addEvents({
57960         /**
57961              * @event beforerowselect
57962              * Fires before a cell is selected.
57963              * @param {SelectionModel} this
57964              * @param {Number} rowIndex The selected row index
57965              * @param {Number} colIndex The selected cell index
57966              */
57967             "beforecellselect" : true,
57968         /**
57969              * @event cellselect
57970              * Fires when a cell is selected.
57971              * @param {SelectionModel} this
57972              * @param {Number} rowIndex The selected row index
57973              * @param {Number} colIndex The selected cell index
57974              */
57975             "cellselect" : true,
57976         /**
57977              * @event selectionchange
57978              * Fires when the active selection changes.
57979              * @param {SelectionModel} this
57980              * @param {Object} selection null for no selection or an object (o) with two properties
57981                 <ul>
57982                 <li>o.record: the record object for the row the selection is in</li>
57983                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
57984                 </ul>
57985              */
57986             "selectionchange" : true,
57987         /**
57988              * @event tabend
57989              * Fires when the tab (or enter) was pressed on the last editable cell
57990              * You can use this to trigger add new row.
57991              * @param {SelectionModel} this
57992              */
57993             "tabend" : true,
57994          /**
57995              * @event beforeeditnext
57996              * Fires before the next editable sell is made active
57997              * You can use this to skip to another cell or fire the tabend
57998              *    if you set cell to false
57999              * @param {Object} eventdata object : { cell : [ row, col ] } 
58000              */
58001             "beforeeditnext" : true
58002     });
58003     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58004 };
58005
58006 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58007     
58008     enter_is_tab: false,
58009
58010     /** @ignore */
58011     initEvents : function(){
58012         this.grid.on("mousedown", this.handleMouseDown, this);
58013         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58014         var view = this.grid.view;
58015         view.on("refresh", this.onViewChange, this);
58016         view.on("rowupdated", this.onRowUpdated, this);
58017         view.on("beforerowremoved", this.clearSelections, this);
58018         view.on("beforerowsinserted", this.clearSelections, this);
58019         if(this.grid.isEditor){
58020             this.grid.on("beforeedit", this.beforeEdit,  this);
58021         }
58022     },
58023
58024         //private
58025     beforeEdit : function(e){
58026         this.select(e.row, e.column, false, true, e.record);
58027     },
58028
58029         //private
58030     onRowUpdated : function(v, index, r){
58031         if(this.selection && this.selection.record == r){
58032             v.onCellSelect(index, this.selection.cell[1]);
58033         }
58034     },
58035
58036         //private
58037     onViewChange : function(){
58038         this.clearSelections(true);
58039     },
58040
58041         /**
58042          * Returns the currently selected cell,.
58043          * @return {Array} The selected cell (row, column) or null if none selected.
58044          */
58045     getSelectedCell : function(){
58046         return this.selection ? this.selection.cell : null;
58047     },
58048
58049     /**
58050      * Clears all selections.
58051      * @param {Boolean} true to prevent the gridview from being notified about the change.
58052      */
58053     clearSelections : function(preventNotify){
58054         var s = this.selection;
58055         if(s){
58056             if(preventNotify !== true){
58057                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58058             }
58059             this.selection = null;
58060             this.fireEvent("selectionchange", this, null);
58061         }
58062     },
58063
58064     /**
58065      * Returns true if there is a selection.
58066      * @return {Boolean}
58067      */
58068     hasSelection : function(){
58069         return this.selection ? true : false;
58070     },
58071
58072     /** @ignore */
58073     handleMouseDown : function(e, t){
58074         var v = this.grid.getView();
58075         if(this.isLocked()){
58076             return;
58077         };
58078         var row = v.findRowIndex(t);
58079         var cell = v.findCellIndex(t);
58080         if(row !== false && cell !== false){
58081             this.select(row, cell);
58082         }
58083     },
58084
58085     /**
58086      * Selects a cell.
58087      * @param {Number} rowIndex
58088      * @param {Number} collIndex
58089      */
58090     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58091         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58092             this.clearSelections();
58093             r = r || this.grid.dataSource.getAt(rowIndex);
58094             this.selection = {
58095                 record : r,
58096                 cell : [rowIndex, colIndex]
58097             };
58098             if(!preventViewNotify){
58099                 var v = this.grid.getView();
58100                 v.onCellSelect(rowIndex, colIndex);
58101                 if(preventFocus !== true){
58102                     v.focusCell(rowIndex, colIndex);
58103                 }
58104             }
58105             this.fireEvent("cellselect", this, rowIndex, colIndex);
58106             this.fireEvent("selectionchange", this, this.selection);
58107         }
58108     },
58109
58110         //private
58111     isSelectable : function(rowIndex, colIndex, cm){
58112         return !cm.isHidden(colIndex);
58113     },
58114
58115     /** @ignore */
58116     handleKeyDown : function(e){
58117         //Roo.log('Cell Sel Model handleKeyDown');
58118         if(!e.isNavKeyPress()){
58119             return;
58120         }
58121         var g = this.grid, s = this.selection;
58122         if(!s){
58123             e.stopEvent();
58124             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58125             if(cell){
58126                 this.select(cell[0], cell[1]);
58127             }
58128             return;
58129         }
58130         var sm = this;
58131         var walk = function(row, col, step){
58132             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58133         };
58134         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58135         var newCell;
58136
58137       
58138
58139         switch(k){
58140             case e.TAB:
58141                 // handled by onEditorKey
58142                 if (g.isEditor && g.editing) {
58143                     return;
58144                 }
58145                 if(e.shiftKey) {
58146                     newCell = walk(r, c-1, -1);
58147                 } else {
58148                     newCell = walk(r, c+1, 1);
58149                 }
58150                 break;
58151             
58152             case e.DOWN:
58153                newCell = walk(r+1, c, 1);
58154                 break;
58155             
58156             case e.UP:
58157                 newCell = walk(r-1, c, -1);
58158                 break;
58159             
58160             case e.RIGHT:
58161                 newCell = walk(r, c+1, 1);
58162                 break;
58163             
58164             case e.LEFT:
58165                 newCell = walk(r, c-1, -1);
58166                 break;
58167             
58168             case e.ENTER:
58169                 
58170                 if(g.isEditor && !g.editing){
58171                    g.startEditing(r, c);
58172                    e.stopEvent();
58173                    return;
58174                 }
58175                 
58176                 
58177              break;
58178         };
58179         if(newCell){
58180             this.select(newCell[0], newCell[1]);
58181             e.stopEvent();
58182             
58183         }
58184     },
58185
58186     acceptsNav : function(row, col, cm){
58187         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58188     },
58189     /**
58190      * Selects a cell.
58191      * @param {Number} field (not used) - as it's normally used as a listener
58192      * @param {Number} e - event - fake it by using
58193      *
58194      * var e = Roo.EventObjectImpl.prototype;
58195      * e.keyCode = e.TAB
58196      *
58197      * 
58198      */
58199     onEditorKey : function(field, e){
58200         
58201         var k = e.getKey(),
58202             newCell,
58203             g = this.grid,
58204             ed = g.activeEditor,
58205             forward = false;
58206         ///Roo.log('onEditorKey' + k);
58207         
58208         
58209         if (this.enter_is_tab && k == e.ENTER) {
58210             k = e.TAB;
58211         }
58212         
58213         if(k == e.TAB){
58214             if(e.shiftKey){
58215                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58216             }else{
58217                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58218                 forward = true;
58219             }
58220             
58221             e.stopEvent();
58222             
58223         } else if(k == e.ENTER &&  !e.ctrlKey){
58224             ed.completeEdit();
58225             e.stopEvent();
58226             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58227         
58228                 } else if(k == e.ESC){
58229             ed.cancelEdit();
58230         }
58231                 
58232         if (newCell) {
58233             var ecall = { cell : newCell, forward : forward };
58234             this.fireEvent('beforeeditnext', ecall );
58235             newCell = ecall.cell;
58236                         forward = ecall.forward;
58237         }
58238                 
58239         if(newCell){
58240             //Roo.log('next cell after edit');
58241             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58242         } else if (forward) {
58243             // tabbed past last
58244             this.fireEvent.defer(100, this, ['tabend',this]);
58245         }
58246     }
58247 });/*
58248  * Based on:
58249  * Ext JS Library 1.1.1
58250  * Copyright(c) 2006-2007, Ext JS, LLC.
58251  *
58252  * Originally Released Under LGPL - original licence link has changed is not relivant.
58253  *
58254  * Fork - LGPL
58255  * <script type="text/javascript">
58256  */
58257  
58258 /**
58259  * @class Roo.grid.EditorGrid
58260  * @extends Roo.grid.Grid
58261  * Class for creating and editable grid.
58262  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58263  * The container MUST have some type of size defined for the grid to fill. The container will be 
58264  * automatically set to position relative if it isn't already.
58265  * @param {Object} dataSource The data model to bind to
58266  * @param {Object} colModel The column model with info about this grid's columns
58267  */
58268 Roo.grid.EditorGrid = function(container, config){
58269     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58270     this.getGridEl().addClass("xedit-grid");
58271
58272     if(!this.selModel){
58273         this.selModel = new Roo.grid.CellSelectionModel();
58274     }
58275
58276     this.activeEditor = null;
58277
58278         this.addEvents({
58279             /**
58280              * @event beforeedit
58281              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58282              * <ul style="padding:5px;padding-left:16px;">
58283              * <li>grid - This grid</li>
58284              * <li>record - The record being edited</li>
58285              * <li>field - The field name being edited</li>
58286              * <li>value - The value for the field being edited.</li>
58287              * <li>row - The grid row index</li>
58288              * <li>column - The grid column index</li>
58289              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58290              * </ul>
58291              * @param {Object} e An edit event (see above for description)
58292              */
58293             "beforeedit" : true,
58294             /**
58295              * @event afteredit
58296              * Fires after a cell is edited. <br />
58297              * <ul style="padding:5px;padding-left:16px;">
58298              * <li>grid - This grid</li>
58299              * <li>record - The record being edited</li>
58300              * <li>field - The field name being edited</li>
58301              * <li>value - The value being set</li>
58302              * <li>originalValue - The original value for the field, before the edit.</li>
58303              * <li>row - The grid row index</li>
58304              * <li>column - The grid column index</li>
58305              * </ul>
58306              * @param {Object} e An edit event (see above for description)
58307              */
58308             "afteredit" : true,
58309             /**
58310              * @event validateedit
58311              * Fires after a cell is edited, but before the value is set in the record. 
58312          * You can use this to modify the value being set in the field, Return false
58313              * to cancel the change. The edit event object has the following properties <br />
58314              * <ul style="padding:5px;padding-left:16px;">
58315          * <li>editor - This editor</li>
58316              * <li>grid - This grid</li>
58317              * <li>record - The record being edited</li>
58318              * <li>field - The field name being edited</li>
58319              * <li>value - The value being set</li>
58320              * <li>originalValue - The original value for the field, before the edit.</li>
58321              * <li>row - The grid row index</li>
58322              * <li>column - The grid column index</li>
58323              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58324              * </ul>
58325              * @param {Object} e An edit event (see above for description)
58326              */
58327             "validateedit" : true
58328         });
58329     this.on("bodyscroll", this.stopEditing,  this);
58330     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58331 };
58332
58333 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58334     /**
58335      * @cfg {Number} clicksToEdit
58336      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58337      */
58338     clicksToEdit: 2,
58339
58340     // private
58341     isEditor : true,
58342     // private
58343     trackMouseOver: false, // causes very odd FF errors
58344
58345     onCellDblClick : function(g, row, col){
58346         this.startEditing(row, col);
58347     },
58348
58349     onEditComplete : function(ed, value, startValue){
58350         this.editing = false;
58351         this.activeEditor = null;
58352         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58353         var r = ed.record;
58354         var field = this.colModel.getDataIndex(ed.col);
58355         var e = {
58356             grid: this,
58357             record: r,
58358             field: field,
58359             originalValue: startValue,
58360             value: value,
58361             row: ed.row,
58362             column: ed.col,
58363             cancel:false,
58364             editor: ed
58365         };
58366         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58367         cell.show();
58368           
58369         if(String(value) !== String(startValue)){
58370             
58371             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58372                 r.set(field, e.value);
58373                 // if we are dealing with a combo box..
58374                 // then we also set the 'name' colum to be the displayField
58375                 if (ed.field.displayField && ed.field.name) {
58376                     r.set(ed.field.name, ed.field.el.dom.value);
58377                 }
58378                 
58379                 delete e.cancel; //?? why!!!
58380                 this.fireEvent("afteredit", e);
58381             }
58382         } else {
58383             this.fireEvent("afteredit", e); // always fire it!
58384         }
58385         this.view.focusCell(ed.row, ed.col);
58386     },
58387
58388     /**
58389      * Starts editing the specified for the specified row/column
58390      * @param {Number} rowIndex
58391      * @param {Number} colIndex
58392      */
58393     startEditing : function(row, col){
58394         this.stopEditing();
58395         if(this.colModel.isCellEditable(col, row)){
58396             this.view.ensureVisible(row, col, true);
58397           
58398             var r = this.dataSource.getAt(row);
58399             var field = this.colModel.getDataIndex(col);
58400             var cell = Roo.get(this.view.getCell(row,col));
58401             var e = {
58402                 grid: this,
58403                 record: r,
58404                 field: field,
58405                 value: r.data[field],
58406                 row: row,
58407                 column: col,
58408                 cancel:false 
58409             };
58410             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58411                 this.editing = true;
58412                 var ed = this.colModel.getCellEditor(col, row);
58413                 
58414                 if (!ed) {
58415                     return;
58416                 }
58417                 if(!ed.rendered){
58418                     ed.render(ed.parentEl || document.body);
58419                 }
58420                 ed.field.reset();
58421                
58422                 cell.hide();
58423                 
58424                 (function(){ // complex but required for focus issues in safari, ie and opera
58425                     ed.row = row;
58426                     ed.col = col;
58427                     ed.record = r;
58428                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58429                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58430                     this.activeEditor = ed;
58431                     var v = r.data[field];
58432                     ed.startEdit(this.view.getCell(row, col), v);
58433                     // combo's with 'displayField and name set
58434                     if (ed.field.displayField && ed.field.name) {
58435                         ed.field.el.dom.value = r.data[ed.field.name];
58436                     }
58437                     
58438                     
58439                 }).defer(50, this);
58440             }
58441         }
58442     },
58443         
58444     /**
58445      * Stops any active editing
58446      */
58447     stopEditing : function(){
58448         if(this.activeEditor){
58449             this.activeEditor.completeEdit();
58450         }
58451         this.activeEditor = null;
58452     },
58453         
58454          /**
58455      * Called to get grid's drag proxy text, by default returns this.ddText.
58456      * @return {String}
58457      */
58458     getDragDropText : function(){
58459         var count = this.selModel.getSelectedCell() ? 1 : 0;
58460         return String.format(this.ddText, count, count == 1 ? '' : 's');
58461     }
58462         
58463 });/*
58464  * Based on:
58465  * Ext JS Library 1.1.1
58466  * Copyright(c) 2006-2007, Ext JS, LLC.
58467  *
58468  * Originally Released Under LGPL - original licence link has changed is not relivant.
58469  *
58470  * Fork - LGPL
58471  * <script type="text/javascript">
58472  */
58473
58474 // private - not really -- you end up using it !
58475 // This is a support class used internally by the Grid components
58476
58477 /**
58478  * @class Roo.grid.GridEditor
58479  * @extends Roo.Editor
58480  * Class for creating and editable grid elements.
58481  * @param {Object} config any settings (must include field)
58482  */
58483 Roo.grid.GridEditor = function(field, config){
58484     if (!config && field.field) {
58485         config = field;
58486         field = Roo.factory(config.field, Roo.form);
58487     }
58488     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58489     field.monitorTab = false;
58490 };
58491
58492 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58493     
58494     /**
58495      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58496      */
58497     
58498     alignment: "tl-tl",
58499     autoSize: "width",
58500     hideEl : false,
58501     cls: "x-small-editor x-grid-editor",
58502     shim:false,
58503     shadow:"frame"
58504 });/*
58505  * Based on:
58506  * Ext JS Library 1.1.1
58507  * Copyright(c) 2006-2007, Ext JS, LLC.
58508  *
58509  * Originally Released Under LGPL - original licence link has changed is not relivant.
58510  *
58511  * Fork - LGPL
58512  * <script type="text/javascript">
58513  */
58514   
58515
58516   
58517 Roo.grid.PropertyRecord = Roo.data.Record.create([
58518     {name:'name',type:'string'},  'value'
58519 ]);
58520
58521
58522 Roo.grid.PropertyStore = function(grid, source){
58523     this.grid = grid;
58524     this.store = new Roo.data.Store({
58525         recordType : Roo.grid.PropertyRecord
58526     });
58527     this.store.on('update', this.onUpdate,  this);
58528     if(source){
58529         this.setSource(source);
58530     }
58531     Roo.grid.PropertyStore.superclass.constructor.call(this);
58532 };
58533
58534
58535
58536 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58537     setSource : function(o){
58538         this.source = o;
58539         this.store.removeAll();
58540         var data = [];
58541         for(var k in o){
58542             if(this.isEditableValue(o[k])){
58543                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58544             }
58545         }
58546         this.store.loadRecords({records: data}, {}, true);
58547     },
58548
58549     onUpdate : function(ds, record, type){
58550         if(type == Roo.data.Record.EDIT){
58551             var v = record.data['value'];
58552             var oldValue = record.modified['value'];
58553             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58554                 this.source[record.id] = v;
58555                 record.commit();
58556                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58557             }else{
58558                 record.reject();
58559             }
58560         }
58561     },
58562
58563     getProperty : function(row){
58564        return this.store.getAt(row);
58565     },
58566
58567     isEditableValue: function(val){
58568         if(val && val instanceof Date){
58569             return true;
58570         }else if(typeof val == 'object' || typeof val == 'function'){
58571             return false;
58572         }
58573         return true;
58574     },
58575
58576     setValue : function(prop, value){
58577         this.source[prop] = value;
58578         this.store.getById(prop).set('value', value);
58579     },
58580
58581     getSource : function(){
58582         return this.source;
58583     }
58584 });
58585
58586 Roo.grid.PropertyColumnModel = function(grid, store){
58587     this.grid = grid;
58588     var g = Roo.grid;
58589     g.PropertyColumnModel.superclass.constructor.call(this, [
58590         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58591         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58592     ]);
58593     this.store = store;
58594     this.bselect = Roo.DomHelper.append(document.body, {
58595         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58596             {tag: 'option', value: 'true', html: 'true'},
58597             {tag: 'option', value: 'false', html: 'false'}
58598         ]
58599     });
58600     Roo.id(this.bselect);
58601     var f = Roo.form;
58602     this.editors = {
58603         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58604         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58605         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58606         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58607         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58608     };
58609     this.renderCellDelegate = this.renderCell.createDelegate(this);
58610     this.renderPropDelegate = this.renderProp.createDelegate(this);
58611 };
58612
58613 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58614     
58615     
58616     nameText : 'Name',
58617     valueText : 'Value',
58618     
58619     dateFormat : 'm/j/Y',
58620     
58621     
58622     renderDate : function(dateVal){
58623         return dateVal.dateFormat(this.dateFormat);
58624     },
58625
58626     renderBool : function(bVal){
58627         return bVal ? 'true' : 'false';
58628     },
58629
58630     isCellEditable : function(colIndex, rowIndex){
58631         return colIndex == 1;
58632     },
58633
58634     getRenderer : function(col){
58635         return col == 1 ?
58636             this.renderCellDelegate : this.renderPropDelegate;
58637     },
58638
58639     renderProp : function(v){
58640         return this.getPropertyName(v);
58641     },
58642
58643     renderCell : function(val){
58644         var rv = val;
58645         if(val instanceof Date){
58646             rv = this.renderDate(val);
58647         }else if(typeof val == 'boolean'){
58648             rv = this.renderBool(val);
58649         }
58650         return Roo.util.Format.htmlEncode(rv);
58651     },
58652
58653     getPropertyName : function(name){
58654         var pn = this.grid.propertyNames;
58655         return pn && pn[name] ? pn[name] : name;
58656     },
58657
58658     getCellEditor : function(colIndex, rowIndex){
58659         var p = this.store.getProperty(rowIndex);
58660         var n = p.data['name'], val = p.data['value'];
58661         
58662         if(typeof(this.grid.customEditors[n]) == 'string'){
58663             return this.editors[this.grid.customEditors[n]];
58664         }
58665         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58666             return this.grid.customEditors[n];
58667         }
58668         if(val instanceof Date){
58669             return this.editors['date'];
58670         }else if(typeof val == 'number'){
58671             return this.editors['number'];
58672         }else if(typeof val == 'boolean'){
58673             return this.editors['boolean'];
58674         }else{
58675             return this.editors['string'];
58676         }
58677     }
58678 });
58679
58680 /**
58681  * @class Roo.grid.PropertyGrid
58682  * @extends Roo.grid.EditorGrid
58683  * This class represents the  interface of a component based property grid control.
58684  * <br><br>Usage:<pre><code>
58685  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58686       
58687  });
58688  // set any options
58689  grid.render();
58690  * </code></pre>
58691   
58692  * @constructor
58693  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58694  * The container MUST have some type of size defined for the grid to fill. The container will be
58695  * automatically set to position relative if it isn't already.
58696  * @param {Object} config A config object that sets properties on this grid.
58697  */
58698 Roo.grid.PropertyGrid = function(container, config){
58699     config = config || {};
58700     var store = new Roo.grid.PropertyStore(this);
58701     this.store = store;
58702     var cm = new Roo.grid.PropertyColumnModel(this, store);
58703     store.store.sort('name', 'ASC');
58704     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58705         ds: store.store,
58706         cm: cm,
58707         enableColLock:false,
58708         enableColumnMove:false,
58709         stripeRows:false,
58710         trackMouseOver: false,
58711         clicksToEdit:1
58712     }, config));
58713     this.getGridEl().addClass('x-props-grid');
58714     this.lastEditRow = null;
58715     this.on('columnresize', this.onColumnResize, this);
58716     this.addEvents({
58717          /**
58718              * @event beforepropertychange
58719              * Fires before a property changes (return false to stop?)
58720              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58721              * @param {String} id Record Id
58722              * @param {String} newval New Value
58723          * @param {String} oldval Old Value
58724              */
58725         "beforepropertychange": true,
58726         /**
58727              * @event propertychange
58728              * Fires after a property changes
58729              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58730              * @param {String} id Record Id
58731              * @param {String} newval New Value
58732          * @param {String} oldval Old Value
58733              */
58734         "propertychange": true
58735     });
58736     this.customEditors = this.customEditors || {};
58737 };
58738 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58739     
58740      /**
58741      * @cfg {Object} customEditors map of colnames=> custom editors.
58742      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58743      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58744      * false disables editing of the field.
58745          */
58746     
58747       /**
58748      * @cfg {Object} propertyNames map of property Names to their displayed value
58749          */
58750     
58751     render : function(){
58752         Roo.grid.PropertyGrid.superclass.render.call(this);
58753         this.autoSize.defer(100, this);
58754     },
58755
58756     autoSize : function(){
58757         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58758         if(this.view){
58759             this.view.fitColumns();
58760         }
58761     },
58762
58763     onColumnResize : function(){
58764         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58765         this.autoSize();
58766     },
58767     /**
58768      * Sets the data for the Grid
58769      * accepts a Key => Value object of all the elements avaiable.
58770      * @param {Object} data  to appear in grid.
58771      */
58772     setSource : function(source){
58773         this.store.setSource(source);
58774         //this.autoSize();
58775     },
58776     /**
58777      * Gets all the data from the grid.
58778      * @return {Object} data  data stored in grid
58779      */
58780     getSource : function(){
58781         return this.store.getSource();
58782     }
58783 });/*
58784   
58785  * Licence LGPL
58786  
58787  */
58788  
58789 /**
58790  * @class Roo.grid.Calendar
58791  * @extends Roo.util.Grid
58792  * This class extends the Grid to provide a calendar widget
58793  * <br><br>Usage:<pre><code>
58794  var grid = new Roo.grid.Calendar("my-container-id", {
58795      ds: myDataStore,
58796      cm: myColModel,
58797      selModel: mySelectionModel,
58798      autoSizeColumns: true,
58799      monitorWindowResize: false,
58800      trackMouseOver: true
58801      eventstore : real data store..
58802  });
58803  // set any options
58804  grid.render();
58805   
58806   * @constructor
58807  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58808  * The container MUST have some type of size defined for the grid to fill. The container will be
58809  * automatically set to position relative if it isn't already.
58810  * @param {Object} config A config object that sets properties on this grid.
58811  */
58812 Roo.grid.Calendar = function(container, config){
58813         // initialize the container
58814         this.container = Roo.get(container);
58815         this.container.update("");
58816         this.container.setStyle("overflow", "hidden");
58817     this.container.addClass('x-grid-container');
58818
58819     this.id = this.container.id;
58820
58821     Roo.apply(this, config);
58822     // check and correct shorthanded configs
58823     
58824     var rows = [];
58825     var d =1;
58826     for (var r = 0;r < 6;r++) {
58827         
58828         rows[r]=[];
58829         for (var c =0;c < 7;c++) {
58830             rows[r][c]= '';
58831         }
58832     }
58833     if (this.eventStore) {
58834         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58835         this.eventStore.on('load',this.onLoad, this);
58836         this.eventStore.on('beforeload',this.clearEvents, this);
58837          
58838     }
58839     
58840     this.dataSource = new Roo.data.Store({
58841             proxy: new Roo.data.MemoryProxy(rows),
58842             reader: new Roo.data.ArrayReader({}, [
58843                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58844     });
58845
58846     this.dataSource.load();
58847     this.ds = this.dataSource;
58848     this.ds.xmodule = this.xmodule || false;
58849     
58850     
58851     var cellRender = function(v,x,r)
58852     {
58853         return String.format(
58854             '<div class="fc-day  fc-widget-content"><div>' +
58855                 '<div class="fc-event-container"></div>' +
58856                 '<div class="fc-day-number">{0}</div>'+
58857                 
58858                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58859             '</div></div>', v);
58860     
58861     }
58862     
58863     
58864     this.colModel = new Roo.grid.ColumnModel( [
58865         {
58866             xtype: 'ColumnModel',
58867             xns: Roo.grid,
58868             dataIndex : 'weekday0',
58869             header : 'Sunday',
58870             renderer : cellRender
58871         },
58872         {
58873             xtype: 'ColumnModel',
58874             xns: Roo.grid,
58875             dataIndex : 'weekday1',
58876             header : 'Monday',
58877             renderer : cellRender
58878         },
58879         {
58880             xtype: 'ColumnModel',
58881             xns: Roo.grid,
58882             dataIndex : 'weekday2',
58883             header : 'Tuesday',
58884             renderer : cellRender
58885         },
58886         {
58887             xtype: 'ColumnModel',
58888             xns: Roo.grid,
58889             dataIndex : 'weekday3',
58890             header : 'Wednesday',
58891             renderer : cellRender
58892         },
58893         {
58894             xtype: 'ColumnModel',
58895             xns: Roo.grid,
58896             dataIndex : 'weekday4',
58897             header : 'Thursday',
58898             renderer : cellRender
58899         },
58900         {
58901             xtype: 'ColumnModel',
58902             xns: Roo.grid,
58903             dataIndex : 'weekday5',
58904             header : 'Friday',
58905             renderer : cellRender
58906         },
58907         {
58908             xtype: 'ColumnModel',
58909             xns: Roo.grid,
58910             dataIndex : 'weekday6',
58911             header : 'Saturday',
58912             renderer : cellRender
58913         }
58914     ]);
58915     this.cm = this.colModel;
58916     this.cm.xmodule = this.xmodule || false;
58917  
58918         
58919           
58920     //this.selModel = new Roo.grid.CellSelectionModel();
58921     //this.sm = this.selModel;
58922     //this.selModel.init(this);
58923     
58924     
58925     if(this.width){
58926         this.container.setWidth(this.width);
58927     }
58928
58929     if(this.height){
58930         this.container.setHeight(this.height);
58931     }
58932     /** @private */
58933         this.addEvents({
58934         // raw events
58935         /**
58936          * @event click
58937          * The raw click event for the entire grid.
58938          * @param {Roo.EventObject} e
58939          */
58940         "click" : true,
58941         /**
58942          * @event dblclick
58943          * The raw dblclick event for the entire grid.
58944          * @param {Roo.EventObject} e
58945          */
58946         "dblclick" : true,
58947         /**
58948          * @event contextmenu
58949          * The raw contextmenu event for the entire grid.
58950          * @param {Roo.EventObject} e
58951          */
58952         "contextmenu" : true,
58953         /**
58954          * @event mousedown
58955          * The raw mousedown event for the entire grid.
58956          * @param {Roo.EventObject} e
58957          */
58958         "mousedown" : true,
58959         /**
58960          * @event mouseup
58961          * The raw mouseup event for the entire grid.
58962          * @param {Roo.EventObject} e
58963          */
58964         "mouseup" : true,
58965         /**
58966          * @event mouseover
58967          * The raw mouseover event for the entire grid.
58968          * @param {Roo.EventObject} e
58969          */
58970         "mouseover" : true,
58971         /**
58972          * @event mouseout
58973          * The raw mouseout event for the entire grid.
58974          * @param {Roo.EventObject} e
58975          */
58976         "mouseout" : true,
58977         /**
58978          * @event keypress
58979          * The raw keypress event for the entire grid.
58980          * @param {Roo.EventObject} e
58981          */
58982         "keypress" : true,
58983         /**
58984          * @event keydown
58985          * The raw keydown event for the entire grid.
58986          * @param {Roo.EventObject} e
58987          */
58988         "keydown" : true,
58989
58990         // custom events
58991
58992         /**
58993          * @event cellclick
58994          * Fires when a cell is clicked
58995          * @param {Grid} this
58996          * @param {Number} rowIndex
58997          * @param {Number} columnIndex
58998          * @param {Roo.EventObject} e
58999          */
59000         "cellclick" : true,
59001         /**
59002          * @event celldblclick
59003          * Fires when a cell is double clicked
59004          * @param {Grid} this
59005          * @param {Number} rowIndex
59006          * @param {Number} columnIndex
59007          * @param {Roo.EventObject} e
59008          */
59009         "celldblclick" : true,
59010         /**
59011          * @event rowclick
59012          * Fires when a row is clicked
59013          * @param {Grid} this
59014          * @param {Number} rowIndex
59015          * @param {Roo.EventObject} e
59016          */
59017         "rowclick" : true,
59018         /**
59019          * @event rowdblclick
59020          * Fires when a row is double clicked
59021          * @param {Grid} this
59022          * @param {Number} rowIndex
59023          * @param {Roo.EventObject} e
59024          */
59025         "rowdblclick" : true,
59026         /**
59027          * @event headerclick
59028          * Fires when a header is clicked
59029          * @param {Grid} this
59030          * @param {Number} columnIndex
59031          * @param {Roo.EventObject} e
59032          */
59033         "headerclick" : true,
59034         /**
59035          * @event headerdblclick
59036          * Fires when a header cell is double clicked
59037          * @param {Grid} this
59038          * @param {Number} columnIndex
59039          * @param {Roo.EventObject} e
59040          */
59041         "headerdblclick" : true,
59042         /**
59043          * @event rowcontextmenu
59044          * Fires when a row is right clicked
59045          * @param {Grid} this
59046          * @param {Number} rowIndex
59047          * @param {Roo.EventObject} e
59048          */
59049         "rowcontextmenu" : true,
59050         /**
59051          * @event cellcontextmenu
59052          * Fires when a cell is right clicked
59053          * @param {Grid} this
59054          * @param {Number} rowIndex
59055          * @param {Number} cellIndex
59056          * @param {Roo.EventObject} e
59057          */
59058          "cellcontextmenu" : true,
59059         /**
59060          * @event headercontextmenu
59061          * Fires when a header is right clicked
59062          * @param {Grid} this
59063          * @param {Number} columnIndex
59064          * @param {Roo.EventObject} e
59065          */
59066         "headercontextmenu" : true,
59067         /**
59068          * @event bodyscroll
59069          * Fires when the body element is scrolled
59070          * @param {Number} scrollLeft
59071          * @param {Number} scrollTop
59072          */
59073         "bodyscroll" : true,
59074         /**
59075          * @event columnresize
59076          * Fires when the user resizes a column
59077          * @param {Number} columnIndex
59078          * @param {Number} newSize
59079          */
59080         "columnresize" : true,
59081         /**
59082          * @event columnmove
59083          * Fires when the user moves a column
59084          * @param {Number} oldIndex
59085          * @param {Number} newIndex
59086          */
59087         "columnmove" : true,
59088         /**
59089          * @event startdrag
59090          * Fires when row(s) start being dragged
59091          * @param {Grid} this
59092          * @param {Roo.GridDD} dd The drag drop object
59093          * @param {event} e The raw browser event
59094          */
59095         "startdrag" : true,
59096         /**
59097          * @event enddrag
59098          * Fires when a drag operation is complete
59099          * @param {Grid} this
59100          * @param {Roo.GridDD} dd The drag drop object
59101          * @param {event} e The raw browser event
59102          */
59103         "enddrag" : true,
59104         /**
59105          * @event dragdrop
59106          * Fires when dragged row(s) are dropped on a valid DD target
59107          * @param {Grid} this
59108          * @param {Roo.GridDD} dd The drag drop object
59109          * @param {String} targetId The target drag drop object
59110          * @param {event} e The raw browser event
59111          */
59112         "dragdrop" : true,
59113         /**
59114          * @event dragover
59115          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59116          * @param {Grid} this
59117          * @param {Roo.GridDD} dd The drag drop object
59118          * @param {String} targetId The target drag drop object
59119          * @param {event} e The raw browser event
59120          */
59121         "dragover" : true,
59122         /**
59123          * @event dragenter
59124          *  Fires when the dragged row(s) first cross another DD target while being dragged
59125          * @param {Grid} this
59126          * @param {Roo.GridDD} dd The drag drop object
59127          * @param {String} targetId The target drag drop object
59128          * @param {event} e The raw browser event
59129          */
59130         "dragenter" : true,
59131         /**
59132          * @event dragout
59133          * Fires when the dragged row(s) leave another DD target while being dragged
59134          * @param {Grid} this
59135          * @param {Roo.GridDD} dd The drag drop object
59136          * @param {String} targetId The target drag drop object
59137          * @param {event} e The raw browser event
59138          */
59139         "dragout" : true,
59140         /**
59141          * @event rowclass
59142          * Fires when a row is rendered, so you can change add a style to it.
59143          * @param {GridView} gridview   The grid view
59144          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59145          */
59146         'rowclass' : true,
59147
59148         /**
59149          * @event render
59150          * Fires when the grid is rendered
59151          * @param {Grid} grid
59152          */
59153         'render' : true,
59154             /**
59155              * @event select
59156              * Fires when a date is selected
59157              * @param {DatePicker} this
59158              * @param {Date} date The selected date
59159              */
59160         'select': true,
59161         /**
59162              * @event monthchange
59163              * Fires when the displayed month changes 
59164              * @param {DatePicker} this
59165              * @param {Date} date The selected month
59166              */
59167         'monthchange': true,
59168         /**
59169              * @event evententer
59170              * Fires when mouse over an event
59171              * @param {Calendar} this
59172              * @param {event} Event
59173              */
59174         'evententer': true,
59175         /**
59176              * @event eventleave
59177              * Fires when the mouse leaves an
59178              * @param {Calendar} this
59179              * @param {event}
59180              */
59181         'eventleave': true,
59182         /**
59183              * @event eventclick
59184              * Fires when the mouse click an
59185              * @param {Calendar} this
59186              * @param {event}
59187              */
59188         'eventclick': true,
59189         /**
59190              * @event eventrender
59191              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59192              * @param {Calendar} this
59193              * @param {data} data to be modified
59194              */
59195         'eventrender': true
59196         
59197     });
59198
59199     Roo.grid.Grid.superclass.constructor.call(this);
59200     this.on('render', function() {
59201         this.view.el.addClass('x-grid-cal'); 
59202         
59203         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59204
59205     },this);
59206     
59207     if (!Roo.grid.Calendar.style) {
59208         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59209             
59210             
59211             '.x-grid-cal .x-grid-col' :  {
59212                 height: 'auto !important',
59213                 'vertical-align': 'top'
59214             },
59215             '.x-grid-cal  .fc-event-hori' : {
59216                 height: '14px'
59217             }
59218              
59219             
59220         }, Roo.id());
59221     }
59222
59223     
59224     
59225 };
59226 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59227     /**
59228      * @cfg {Store} eventStore The store that loads events.
59229      */
59230     eventStore : 25,
59231
59232      
59233     activeDate : false,
59234     startDay : 0,
59235     autoWidth : true,
59236     monitorWindowResize : false,
59237
59238     
59239     resizeColumns : function() {
59240         var col = (this.view.el.getWidth() / 7) - 3;
59241         // loop through cols, and setWidth
59242         for(var i =0 ; i < 7 ; i++){
59243             this.cm.setColumnWidth(i, col);
59244         }
59245     },
59246      setDate :function(date) {
59247         
59248         Roo.log('setDate?');
59249         
59250         this.resizeColumns();
59251         var vd = this.activeDate;
59252         this.activeDate = date;
59253 //        if(vd && this.el){
59254 //            var t = date.getTime();
59255 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59256 //                Roo.log('using add remove');
59257 //                
59258 //                this.fireEvent('monthchange', this, date);
59259 //                
59260 //                this.cells.removeClass("fc-state-highlight");
59261 //                this.cells.each(function(c){
59262 //                   if(c.dateValue == t){
59263 //                       c.addClass("fc-state-highlight");
59264 //                       setTimeout(function(){
59265 //                            try{c.dom.firstChild.focus();}catch(e){}
59266 //                       }, 50);
59267 //                       return false;
59268 //                   }
59269 //                   return true;
59270 //                });
59271 //                return;
59272 //            }
59273 //        }
59274         
59275         var days = date.getDaysInMonth();
59276         
59277         var firstOfMonth = date.getFirstDateOfMonth();
59278         var startingPos = firstOfMonth.getDay()-this.startDay;
59279         
59280         if(startingPos < this.startDay){
59281             startingPos += 7;
59282         }
59283         
59284         var pm = date.add(Date.MONTH, -1);
59285         var prevStart = pm.getDaysInMonth()-startingPos;
59286 //        
59287         
59288         
59289         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59290         
59291         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59292         //this.cells.addClassOnOver('fc-state-hover');
59293         
59294         var cells = this.cells.elements;
59295         var textEls = this.textNodes;
59296         
59297         //Roo.each(cells, function(cell){
59298         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59299         //});
59300         
59301         days += startingPos;
59302
59303         // convert everything to numbers so it's fast
59304         var day = 86400000;
59305         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59306         //Roo.log(d);
59307         //Roo.log(pm);
59308         //Roo.log(prevStart);
59309         
59310         var today = new Date().clearTime().getTime();
59311         var sel = date.clearTime().getTime();
59312         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59313         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59314         var ddMatch = this.disabledDatesRE;
59315         var ddText = this.disabledDatesText;
59316         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59317         var ddaysText = this.disabledDaysText;
59318         var format = this.format;
59319         
59320         var setCellClass = function(cal, cell){
59321             
59322             //Roo.log('set Cell Class');
59323             cell.title = "";
59324             var t = d.getTime();
59325             
59326             //Roo.log(d);
59327             
59328             
59329             cell.dateValue = t;
59330             if(t == today){
59331                 cell.className += " fc-today";
59332                 cell.className += " fc-state-highlight";
59333                 cell.title = cal.todayText;
59334             }
59335             if(t == sel){
59336                 // disable highlight in other month..
59337                 cell.className += " fc-state-highlight";
59338                 
59339             }
59340             // disabling
59341             if(t < min) {
59342                 //cell.className = " fc-state-disabled";
59343                 cell.title = cal.minText;
59344                 return;
59345             }
59346             if(t > max) {
59347                 //cell.className = " fc-state-disabled";
59348                 cell.title = cal.maxText;
59349                 return;
59350             }
59351             if(ddays){
59352                 if(ddays.indexOf(d.getDay()) != -1){
59353                     // cell.title = ddaysText;
59354                    // cell.className = " fc-state-disabled";
59355                 }
59356             }
59357             if(ddMatch && format){
59358                 var fvalue = d.dateFormat(format);
59359                 if(ddMatch.test(fvalue)){
59360                     cell.title = ddText.replace("%0", fvalue);
59361                    cell.className = " fc-state-disabled";
59362                 }
59363             }
59364             
59365             if (!cell.initialClassName) {
59366                 cell.initialClassName = cell.dom.className;
59367             }
59368             
59369             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59370         };
59371
59372         var i = 0;
59373         
59374         for(; i < startingPos; i++) {
59375             cells[i].dayName =  (++prevStart);
59376             Roo.log(textEls[i]);
59377             d.setDate(d.getDate()+1);
59378             
59379             //cells[i].className = "fc-past fc-other-month";
59380             setCellClass(this, cells[i]);
59381         }
59382         
59383         var intDay = 0;
59384         
59385         for(; i < days; i++){
59386             intDay = i - startingPos + 1;
59387             cells[i].dayName =  (intDay);
59388             d.setDate(d.getDate()+1);
59389             
59390             cells[i].className = ''; // "x-date-active";
59391             setCellClass(this, cells[i]);
59392         }
59393         var extraDays = 0;
59394         
59395         for(; i < 42; i++) {
59396             //textEls[i].innerHTML = (++extraDays);
59397             
59398             d.setDate(d.getDate()+1);
59399             cells[i].dayName = (++extraDays);
59400             cells[i].className = "fc-future fc-other-month";
59401             setCellClass(this, cells[i]);
59402         }
59403         
59404         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59405         
59406         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59407         
59408         // this will cause all the cells to mis
59409         var rows= [];
59410         var i =0;
59411         for (var r = 0;r < 6;r++) {
59412             for (var c =0;c < 7;c++) {
59413                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59414             }    
59415         }
59416         
59417         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59418         for(i=0;i<cells.length;i++) {
59419             
59420             this.cells.elements[i].dayName = cells[i].dayName ;
59421             this.cells.elements[i].className = cells[i].className;
59422             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59423             this.cells.elements[i].title = cells[i].title ;
59424             this.cells.elements[i].dateValue = cells[i].dateValue ;
59425         }
59426         
59427         
59428         
59429         
59430         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59431         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59432         
59433         ////if(totalRows != 6){
59434             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59435            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59436        // }
59437         
59438         this.fireEvent('monthchange', this, date);
59439         
59440         
59441     },
59442  /**
59443      * Returns the grid's SelectionModel.
59444      * @return {SelectionModel}
59445      */
59446     getSelectionModel : function(){
59447         if(!this.selModel){
59448             this.selModel = new Roo.grid.CellSelectionModel();
59449         }
59450         return this.selModel;
59451     },
59452
59453     load: function() {
59454         this.eventStore.load()
59455         
59456         
59457         
59458     },
59459     
59460     findCell : function(dt) {
59461         dt = dt.clearTime().getTime();
59462         var ret = false;
59463         this.cells.each(function(c){
59464             //Roo.log("check " +c.dateValue + '?=' + dt);
59465             if(c.dateValue == dt){
59466                 ret = c;
59467                 return false;
59468             }
59469             return true;
59470         });
59471         
59472         return ret;
59473     },
59474     
59475     findCells : function(rec) {
59476         var s = rec.data.start_dt.clone().clearTime().getTime();
59477        // Roo.log(s);
59478         var e= rec.data.end_dt.clone().clearTime().getTime();
59479        // Roo.log(e);
59480         var ret = [];
59481         this.cells.each(function(c){
59482              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59483             
59484             if(c.dateValue > e){
59485                 return ;
59486             }
59487             if(c.dateValue < s){
59488                 return ;
59489             }
59490             ret.push(c);
59491         });
59492         
59493         return ret;    
59494     },
59495     
59496     findBestRow: function(cells)
59497     {
59498         var ret = 0;
59499         
59500         for (var i =0 ; i < cells.length;i++) {
59501             ret  = Math.max(cells[i].rows || 0,ret);
59502         }
59503         return ret;
59504         
59505     },
59506     
59507     
59508     addItem : function(rec)
59509     {
59510         // look for vertical location slot in
59511         var cells = this.findCells(rec);
59512         
59513         rec.row = this.findBestRow(cells);
59514         
59515         // work out the location.
59516         
59517         var crow = false;
59518         var rows = [];
59519         for(var i =0; i < cells.length; i++) {
59520             if (!crow) {
59521                 crow = {
59522                     start : cells[i],
59523                     end :  cells[i]
59524                 };
59525                 continue;
59526             }
59527             if (crow.start.getY() == cells[i].getY()) {
59528                 // on same row.
59529                 crow.end = cells[i];
59530                 continue;
59531             }
59532             // different row.
59533             rows.push(crow);
59534             crow = {
59535                 start: cells[i],
59536                 end : cells[i]
59537             };
59538             
59539         }
59540         
59541         rows.push(crow);
59542         rec.els = [];
59543         rec.rows = rows;
59544         rec.cells = cells;
59545         for (var i = 0; i < cells.length;i++) {
59546             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59547             
59548         }
59549         
59550         
59551     },
59552     
59553     clearEvents: function() {
59554         
59555         if (!this.eventStore.getCount()) {
59556             return;
59557         }
59558         // reset number of rows in cells.
59559         Roo.each(this.cells.elements, function(c){
59560             c.rows = 0;
59561         });
59562         
59563         this.eventStore.each(function(e) {
59564             this.clearEvent(e);
59565         },this);
59566         
59567     },
59568     
59569     clearEvent : function(ev)
59570     {
59571         if (ev.els) {
59572             Roo.each(ev.els, function(el) {
59573                 el.un('mouseenter' ,this.onEventEnter, this);
59574                 el.un('mouseleave' ,this.onEventLeave, this);
59575                 el.remove();
59576             },this);
59577             ev.els = [];
59578         }
59579     },
59580     
59581     
59582     renderEvent : function(ev,ctr) {
59583         if (!ctr) {
59584              ctr = this.view.el.select('.fc-event-container',true).first();
59585         }
59586         
59587          
59588         this.clearEvent(ev);
59589             //code
59590        
59591         
59592         
59593         ev.els = [];
59594         var cells = ev.cells;
59595         var rows = ev.rows;
59596         this.fireEvent('eventrender', this, ev);
59597         
59598         for(var i =0; i < rows.length; i++) {
59599             
59600             cls = '';
59601             if (i == 0) {
59602                 cls += ' fc-event-start';
59603             }
59604             if ((i+1) == rows.length) {
59605                 cls += ' fc-event-end';
59606             }
59607             
59608             //Roo.log(ev.data);
59609             // how many rows should it span..
59610             var cg = this.eventTmpl.append(ctr,Roo.apply({
59611                 fccls : cls
59612                 
59613             }, ev.data) , true);
59614             
59615             
59616             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59617             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59618             cg.on('click', this.onEventClick, this, ev);
59619             
59620             ev.els.push(cg);
59621             
59622             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59623             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59624             //Roo.log(cg);
59625              
59626             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59627             cg.setWidth(ebox.right - sbox.x -2);
59628         }
59629     },
59630     
59631     renderEvents: function()
59632     {   
59633         // first make sure there is enough space..
59634         
59635         if (!this.eventTmpl) {
59636             this.eventTmpl = new Roo.Template(
59637                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59638                     '<div class="fc-event-inner">' +
59639                         '<span class="fc-event-time">{time}</span>' +
59640                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59641                     '</div>' +
59642                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59643                 '</div>'
59644             );
59645                 
59646         }
59647                
59648         
59649         
59650         this.cells.each(function(c) {
59651             //Roo.log(c.select('.fc-day-content div',true).first());
59652             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59653         });
59654         
59655         var ctr = this.view.el.select('.fc-event-container',true).first();
59656         
59657         var cls;
59658         this.eventStore.each(function(ev){
59659             
59660             this.renderEvent(ev);
59661              
59662              
59663         }, this);
59664         this.view.layout();
59665         
59666     },
59667     
59668     onEventEnter: function (e, el,event,d) {
59669         this.fireEvent('evententer', this, el, event);
59670     },
59671     
59672     onEventLeave: function (e, el,event,d) {
59673         this.fireEvent('eventleave', this, el, event);
59674     },
59675     
59676     onEventClick: function (e, el,event,d) {
59677         this.fireEvent('eventclick', this, el, event);
59678     },
59679     
59680     onMonthChange: function () {
59681         this.store.load();
59682     },
59683     
59684     onLoad: function () {
59685         
59686         //Roo.log('calendar onload');
59687 //         
59688         if(this.eventStore.getCount() > 0){
59689             
59690            
59691             
59692             this.eventStore.each(function(d){
59693                 
59694                 
59695                 // FIXME..
59696                 var add =   d.data;
59697                 if (typeof(add.end_dt) == 'undefined')  {
59698                     Roo.log("Missing End time in calendar data: ");
59699                     Roo.log(d);
59700                     return;
59701                 }
59702                 if (typeof(add.start_dt) == 'undefined')  {
59703                     Roo.log("Missing Start time in calendar data: ");
59704                     Roo.log(d);
59705                     return;
59706                 }
59707                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59708                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59709                 add.id = add.id || d.id;
59710                 add.title = add.title || '??';
59711                 
59712                 this.addItem(d);
59713                 
59714              
59715             },this);
59716         }
59717         
59718         this.renderEvents();
59719     }
59720     
59721
59722 });
59723 /*
59724  grid : {
59725                 xtype: 'Grid',
59726                 xns: Roo.grid,
59727                 listeners : {
59728                     render : function ()
59729                     {
59730                         _this.grid = this;
59731                         
59732                         if (!this.view.el.hasClass('course-timesheet')) {
59733                             this.view.el.addClass('course-timesheet');
59734                         }
59735                         if (this.tsStyle) {
59736                             this.ds.load({});
59737                             return; 
59738                         }
59739                         Roo.log('width');
59740                         Roo.log(_this.grid.view.el.getWidth());
59741                         
59742                         
59743                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59744                             '.course-timesheet .x-grid-row' : {
59745                                 height: '80px'
59746                             },
59747                             '.x-grid-row td' : {
59748                                 'vertical-align' : 0
59749                             },
59750                             '.course-edit-link' : {
59751                                 'color' : 'blue',
59752                                 'text-overflow' : 'ellipsis',
59753                                 'overflow' : 'hidden',
59754                                 'white-space' : 'nowrap',
59755                                 'cursor' : 'pointer'
59756                             },
59757                             '.sub-link' : {
59758                                 'color' : 'green'
59759                             },
59760                             '.de-act-sup-link' : {
59761                                 'color' : 'purple',
59762                                 'text-decoration' : 'line-through'
59763                             },
59764                             '.de-act-link' : {
59765                                 'color' : 'red',
59766                                 'text-decoration' : 'line-through'
59767                             },
59768                             '.course-timesheet .course-highlight' : {
59769                                 'border-top-style': 'dashed !important',
59770                                 'border-bottom-bottom': 'dashed !important'
59771                             },
59772                             '.course-timesheet .course-item' : {
59773                                 'font-family'   : 'tahoma, arial, helvetica',
59774                                 'font-size'     : '11px',
59775                                 'overflow'      : 'hidden',
59776                                 'padding-left'  : '10px',
59777                                 'padding-right' : '10px',
59778                                 'padding-top' : '10px' 
59779                             }
59780                             
59781                         }, Roo.id());
59782                                 this.ds.load({});
59783                     }
59784                 },
59785                 autoWidth : true,
59786                 monitorWindowResize : false,
59787                 cellrenderer : function(v,x,r)
59788                 {
59789                     return v;
59790                 },
59791                 sm : {
59792                     xtype: 'CellSelectionModel',
59793                     xns: Roo.grid
59794                 },
59795                 dataSource : {
59796                     xtype: 'Store',
59797                     xns: Roo.data,
59798                     listeners : {
59799                         beforeload : function (_self, options)
59800                         {
59801                             options.params = options.params || {};
59802                             options.params._month = _this.monthField.getValue();
59803                             options.params.limit = 9999;
59804                             options.params['sort'] = 'when_dt';    
59805                             options.params['dir'] = 'ASC';    
59806                             this.proxy.loadResponse = this.loadResponse;
59807                             Roo.log("load?");
59808                             //this.addColumns();
59809                         },
59810                         load : function (_self, records, options)
59811                         {
59812                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59813                                 // if you click on the translation.. you can edit it...
59814                                 var el = Roo.get(this);
59815                                 var id = el.dom.getAttribute('data-id');
59816                                 var d = el.dom.getAttribute('data-date');
59817                                 var t = el.dom.getAttribute('data-time');
59818                                 //var id = this.child('span').dom.textContent;
59819                                 
59820                                 //Roo.log(this);
59821                                 Pman.Dialog.CourseCalendar.show({
59822                                     id : id,
59823                                     when_d : d,
59824                                     when_t : t,
59825                                     productitem_active : id ? 1 : 0
59826                                 }, function() {
59827                                     _this.grid.ds.load({});
59828                                 });
59829                            
59830                            });
59831                            
59832                            _this.panel.fireEvent('resize', [ '', '' ]);
59833                         }
59834                     },
59835                     loadResponse : function(o, success, response){
59836                             // this is overridden on before load..
59837                             
59838                             Roo.log("our code?");       
59839                             //Roo.log(success);
59840                             //Roo.log(response)
59841                             delete this.activeRequest;
59842                             if(!success){
59843                                 this.fireEvent("loadexception", this, o, response);
59844                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59845                                 return;
59846                             }
59847                             var result;
59848                             try {
59849                                 result = o.reader.read(response);
59850                             }catch(e){
59851                                 Roo.log("load exception?");
59852                                 this.fireEvent("loadexception", this, o, response, e);
59853                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59854                                 return;
59855                             }
59856                             Roo.log("ready...");        
59857                             // loop through result.records;
59858                             // and set this.tdate[date] = [] << array of records..
59859                             _this.tdata  = {};
59860                             Roo.each(result.records, function(r){
59861                                 //Roo.log(r.data);
59862                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59863                                     _this.tdata[r.data.when_dt.format('j')] = [];
59864                                 }
59865                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59866                             });
59867                             
59868                             //Roo.log(_this.tdata);
59869                             
59870                             result.records = [];
59871                             result.totalRecords = 6;
59872                     
59873                             // let's generate some duumy records for the rows.
59874                             //var st = _this.dateField.getValue();
59875                             
59876                             // work out monday..
59877                             //st = st.add(Date.DAY, -1 * st.format('w'));
59878                             
59879                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59880                             
59881                             var firstOfMonth = date.getFirstDayOfMonth();
59882                             var days = date.getDaysInMonth();
59883                             var d = 1;
59884                             var firstAdded = false;
59885                             for (var i = 0; i < result.totalRecords ; i++) {
59886                                 //var d= st.add(Date.DAY, i);
59887                                 var row = {};
59888                                 var added = 0;
59889                                 for(var w = 0 ; w < 7 ; w++){
59890                                     if(!firstAdded && firstOfMonth != w){
59891                                         continue;
59892                                     }
59893                                     if(d > days){
59894                                         continue;
59895                                     }
59896                                     firstAdded = true;
59897                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59898                                     row['weekday'+w] = String.format(
59899                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59900                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59901                                                     d,
59902                                                     date.format('Y-m-')+dd
59903                                                 );
59904                                     added++;
59905                                     if(typeof(_this.tdata[d]) != 'undefined'){
59906                                         Roo.each(_this.tdata[d], function(r){
59907                                             var is_sub = '';
59908                                             var deactive = '';
59909                                             var id = r.id;
59910                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59911                                             if(r.parent_id*1>0){
59912                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59913                                                 id = r.parent_id;
59914                                             }
59915                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59916                                                 deactive = 'de-act-link';
59917                                             }
59918                                             
59919                                             row['weekday'+w] += String.format(
59920                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59921                                                     id, //0
59922                                                     r.product_id_name, //1
59923                                                     r.when_dt.format('h:ia'), //2
59924                                                     is_sub, //3
59925                                                     deactive, //4
59926                                                     desc // 5
59927                                             );
59928                                         });
59929                                     }
59930                                     d++;
59931                                 }
59932                                 
59933                                 // only do this if something added..
59934                                 if(added > 0){ 
59935                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59936                                 }
59937                                 
59938                                 
59939                                 // push it twice. (second one with an hour..
59940                                 
59941                             }
59942                             //Roo.log(result);
59943                             this.fireEvent("load", this, o, o.request.arg);
59944                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59945                         },
59946                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59947                     proxy : {
59948                         xtype: 'HttpProxy',
59949                         xns: Roo.data,
59950                         method : 'GET',
59951                         url : baseURL + '/Roo/Shop_course.php'
59952                     },
59953                     reader : {
59954                         xtype: 'JsonReader',
59955                         xns: Roo.data,
59956                         id : 'id',
59957                         fields : [
59958                             {
59959                                 'name': 'id',
59960                                 'type': 'int'
59961                             },
59962                             {
59963                                 'name': 'when_dt',
59964                                 'type': 'string'
59965                             },
59966                             {
59967                                 'name': 'end_dt',
59968                                 'type': 'string'
59969                             },
59970                             {
59971                                 'name': 'parent_id',
59972                                 'type': 'int'
59973                             },
59974                             {
59975                                 'name': 'product_id',
59976                                 'type': 'int'
59977                             },
59978                             {
59979                                 'name': 'productitem_id',
59980                                 'type': 'int'
59981                             },
59982                             {
59983                                 'name': 'guid',
59984                                 'type': 'int'
59985                             }
59986                         ]
59987                     }
59988                 },
59989                 toolbar : {
59990                     xtype: 'Toolbar',
59991                     xns: Roo,
59992                     items : [
59993                         {
59994                             xtype: 'Button',
59995                             xns: Roo.Toolbar,
59996                             listeners : {
59997                                 click : function (_self, e)
59998                                 {
59999                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60000                                     sd.setMonth(sd.getMonth()-1);
60001                                     _this.monthField.setValue(sd.format('Y-m-d'));
60002                                     _this.grid.ds.load({});
60003                                 }
60004                             },
60005                             text : "Back"
60006                         },
60007                         {
60008                             xtype: 'Separator',
60009                             xns: Roo.Toolbar
60010                         },
60011                         {
60012                             xtype: 'MonthField',
60013                             xns: Roo.form,
60014                             listeners : {
60015                                 render : function (_self)
60016                                 {
60017                                     _this.monthField = _self;
60018                                    // _this.monthField.set  today
60019                                 },
60020                                 select : function (combo, date)
60021                                 {
60022                                     _this.grid.ds.load({});
60023                                 }
60024                             },
60025                             value : (function() { return new Date(); })()
60026                         },
60027                         {
60028                             xtype: 'Separator',
60029                             xns: Roo.Toolbar
60030                         },
60031                         {
60032                             xtype: 'TextItem',
60033                             xns: Roo.Toolbar,
60034                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60035                         },
60036                         {
60037                             xtype: 'Fill',
60038                             xns: Roo.Toolbar
60039                         },
60040                         {
60041                             xtype: 'Button',
60042                             xns: Roo.Toolbar,
60043                             listeners : {
60044                                 click : function (_self, e)
60045                                 {
60046                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60047                                     sd.setMonth(sd.getMonth()+1);
60048                                     _this.monthField.setValue(sd.format('Y-m-d'));
60049                                     _this.grid.ds.load({});
60050                                 }
60051                             },
60052                             text : "Next"
60053                         }
60054                     ]
60055                 },
60056                  
60057             }
60058         };
60059         
60060         *//*
60061  * Based on:
60062  * Ext JS Library 1.1.1
60063  * Copyright(c) 2006-2007, Ext JS, LLC.
60064  *
60065  * Originally Released Under LGPL - original licence link has changed is not relivant.
60066  *
60067  * Fork - LGPL
60068  * <script type="text/javascript">
60069  */
60070  
60071 /**
60072  * @class Roo.LoadMask
60073  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60074  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60075  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60076  * element's UpdateManager load indicator and will be destroyed after the initial load.
60077  * @constructor
60078  * Create a new LoadMask
60079  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60080  * @param {Object} config The config object
60081  */
60082 Roo.LoadMask = function(el, config){
60083     this.el = Roo.get(el);
60084     Roo.apply(this, config);
60085     if(this.store){
60086         this.store.on('beforeload', this.onBeforeLoad, this);
60087         this.store.on('load', this.onLoad, this);
60088         this.store.on('loadexception', this.onLoadException, this);
60089         this.removeMask = false;
60090     }else{
60091         var um = this.el.getUpdateManager();
60092         um.showLoadIndicator = false; // disable the default indicator
60093         um.on('beforeupdate', this.onBeforeLoad, this);
60094         um.on('update', this.onLoad, this);
60095         um.on('failure', this.onLoad, this);
60096         this.removeMask = true;
60097     }
60098 };
60099
60100 Roo.LoadMask.prototype = {
60101     /**
60102      * @cfg {Boolean} removeMask
60103      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60104      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60105      */
60106     /**
60107      * @cfg {String} msg
60108      * The text to display in a centered loading message box (defaults to 'Loading...')
60109      */
60110     msg : 'Loading...',
60111     /**
60112      * @cfg {String} msgCls
60113      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60114      */
60115     msgCls : 'x-mask-loading',
60116
60117     /**
60118      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60119      * @type Boolean
60120      */
60121     disabled: false,
60122
60123     /**
60124      * Disables the mask to prevent it from being displayed
60125      */
60126     disable : function(){
60127        this.disabled = true;
60128     },
60129
60130     /**
60131      * Enables the mask so that it can be displayed
60132      */
60133     enable : function(){
60134         this.disabled = false;
60135     },
60136     
60137     onLoadException : function()
60138     {
60139         Roo.log(arguments);
60140         
60141         if (typeof(arguments[3]) != 'undefined') {
60142             Roo.MessageBox.alert("Error loading",arguments[3]);
60143         } 
60144         /*
60145         try {
60146             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60147                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60148             }   
60149         } catch(e) {
60150             
60151         }
60152         */
60153     
60154         
60155         
60156         this.el.unmask(this.removeMask);
60157     },
60158     // private
60159     onLoad : function()
60160     {
60161         this.el.unmask(this.removeMask);
60162     },
60163
60164     // private
60165     onBeforeLoad : function(){
60166         if(!this.disabled){
60167             this.el.mask(this.msg, this.msgCls);
60168         }
60169     },
60170
60171     // private
60172     destroy : function(){
60173         if(this.store){
60174             this.store.un('beforeload', this.onBeforeLoad, this);
60175             this.store.un('load', this.onLoad, this);
60176             this.store.un('loadexception', this.onLoadException, this);
60177         }else{
60178             var um = this.el.getUpdateManager();
60179             um.un('beforeupdate', this.onBeforeLoad, this);
60180             um.un('update', this.onLoad, this);
60181             um.un('failure', this.onLoad, this);
60182         }
60183     }
60184 };/*
60185  * Based on:
60186  * Ext JS Library 1.1.1
60187  * Copyright(c) 2006-2007, Ext JS, LLC.
60188  *
60189  * Originally Released Under LGPL - original licence link has changed is not relivant.
60190  *
60191  * Fork - LGPL
60192  * <script type="text/javascript">
60193  */
60194
60195
60196 /**
60197  * @class Roo.XTemplate
60198  * @extends Roo.Template
60199  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60200 <pre><code>
60201 var t = new Roo.XTemplate(
60202         '&lt;select name="{name}"&gt;',
60203                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60204         '&lt;/select&gt;'
60205 );
60206  
60207 // then append, applying the master template values
60208  </code></pre>
60209  *
60210  * Supported features:
60211  *
60212  *  Tags:
60213
60214 <pre><code>
60215       {a_variable} - output encoded.
60216       {a_variable.format:("Y-m-d")} - call a method on the variable
60217       {a_variable:raw} - unencoded output
60218       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60219       {a_variable:this.method_on_template(...)} - call a method on the template object.
60220  
60221 </code></pre>
60222  *  The tpl tag:
60223 <pre><code>
60224         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60225         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60226         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60227         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60228   
60229         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60230         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60231 </code></pre>
60232  *      
60233  */
60234 Roo.XTemplate = function()
60235 {
60236     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60237     if (this.html) {
60238         this.compile();
60239     }
60240 };
60241
60242
60243 Roo.extend(Roo.XTemplate, Roo.Template, {
60244
60245     /**
60246      * The various sub templates
60247      */
60248     tpls : false,
60249     /**
60250      *
60251      * basic tag replacing syntax
60252      * WORD:WORD()
60253      *
60254      * // you can fake an object call by doing this
60255      *  x.t:(test,tesT) 
60256      * 
60257      */
60258     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60259
60260     /**
60261      * compile the template
60262      *
60263      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60264      *
60265      */
60266     compile: function()
60267     {
60268         var s = this.html;
60269      
60270         s = ['<tpl>', s, '</tpl>'].join('');
60271     
60272         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60273             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60274             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60275             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60276             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60277             m,
60278             id     = 0,
60279             tpls   = [];
60280     
60281         while(true == !!(m = s.match(re))){
60282             var forMatch   = m[0].match(nameRe),
60283                 ifMatch   = m[0].match(ifRe),
60284                 execMatch   = m[0].match(execRe),
60285                 namedMatch   = m[0].match(namedRe),
60286                 
60287                 exp  = null, 
60288                 fn   = null,
60289                 exec = null,
60290                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60291                 
60292             if (ifMatch) {
60293                 // if - puts fn into test..
60294                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60295                 if(exp){
60296                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60297                 }
60298             }
60299             
60300             if (execMatch) {
60301                 // exec - calls a function... returns empty if true is  returned.
60302                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60303                 if(exp){
60304                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60305                 }
60306             }
60307             
60308             
60309             if (name) {
60310                 // for = 
60311                 switch(name){
60312                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60313                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60314                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60315                 }
60316             }
60317             var uid = namedMatch ? namedMatch[1] : id;
60318             
60319             
60320             tpls.push({
60321                 id:     namedMatch ? namedMatch[1] : id,
60322                 target: name,
60323                 exec:   exec,
60324                 test:   fn,
60325                 body:   m[1] || ''
60326             });
60327             if (namedMatch) {
60328                 s = s.replace(m[0], '');
60329             } else { 
60330                 s = s.replace(m[0], '{xtpl'+ id + '}');
60331             }
60332             ++id;
60333         }
60334         this.tpls = [];
60335         for(var i = tpls.length-1; i >= 0; --i){
60336             this.compileTpl(tpls[i]);
60337             this.tpls[tpls[i].id] = tpls[i];
60338         }
60339         this.master = tpls[tpls.length-1];
60340         return this;
60341     },
60342     /**
60343      * same as applyTemplate, except it's done to one of the subTemplates
60344      * when using named templates, you can do:
60345      *
60346      * var str = pl.applySubTemplate('your-name', values);
60347      *
60348      * 
60349      * @param {Number} id of the template
60350      * @param {Object} values to apply to template
60351      * @param {Object} parent (normaly the instance of this object)
60352      */
60353     applySubTemplate : function(id, values, parent)
60354     {
60355         
60356         
60357         var t = this.tpls[id];
60358         
60359         
60360         try { 
60361             if(t.test && !t.test.call(this, values, parent)){
60362                 return '';
60363             }
60364         } catch(e) {
60365             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60366             Roo.log(e.toString());
60367             Roo.log(t.test);
60368             return ''
60369         }
60370         try { 
60371             
60372             if(t.exec && t.exec.call(this, values, parent)){
60373                 return '';
60374             }
60375         } catch(e) {
60376             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60377             Roo.log(e.toString());
60378             Roo.log(t.exec);
60379             return ''
60380         }
60381         try {
60382             var vs = t.target ? t.target.call(this, values, parent) : values;
60383             parent = t.target ? values : parent;
60384             if(t.target && vs instanceof Array){
60385                 var buf = [];
60386                 for(var i = 0, len = vs.length; i < len; i++){
60387                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60388                 }
60389                 return buf.join('');
60390             }
60391             return t.compiled.call(this, vs, parent);
60392         } catch (e) {
60393             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60394             Roo.log(e.toString());
60395             Roo.log(t.compiled);
60396             return '';
60397         }
60398     },
60399
60400     compileTpl : function(tpl)
60401     {
60402         var fm = Roo.util.Format;
60403         var useF = this.disableFormats !== true;
60404         var sep = Roo.isGecko ? "+" : ",";
60405         var undef = function(str) {
60406             Roo.log("Property not found :"  + str);
60407             return '';
60408         };
60409         
60410         var fn = function(m, name, format, args)
60411         {
60412             //Roo.log(arguments);
60413             args = args ? args.replace(/\\'/g,"'") : args;
60414             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60415             if (typeof(format) == 'undefined') {
60416                 format= 'htmlEncode';
60417             }
60418             if (format == 'raw' ) {
60419                 format = false;
60420             }
60421             
60422             if(name.substr(0, 4) == 'xtpl'){
60423                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60424             }
60425             
60426             // build an array of options to determine if value is undefined..
60427             
60428             // basically get 'xxxx.yyyy' then do
60429             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60430             //    (function () { Roo.log("Property not found"); return ''; })() :
60431             //    ......
60432             
60433             var udef_ar = [];
60434             var lookfor = '';
60435             Roo.each(name.split('.'), function(st) {
60436                 lookfor += (lookfor.length ? '.': '') + st;
60437                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60438             });
60439             
60440             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60441             
60442             
60443             if(format && useF){
60444                 
60445                 args = args ? ',' + args : "";
60446                  
60447                 if(format.substr(0, 5) != "this."){
60448                     format = "fm." + format + '(';
60449                 }else{
60450                     format = 'this.call("'+ format.substr(5) + '", ';
60451                     args = ", values";
60452                 }
60453                 
60454                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60455             }
60456              
60457             if (args.length) {
60458                 // called with xxyx.yuu:(test,test)
60459                 // change to ()
60460                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60461             }
60462             // raw.. - :raw modifier..
60463             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60464             
60465         };
60466         var body;
60467         // branched to use + in gecko and [].join() in others
60468         if(Roo.isGecko){
60469             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60470                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60471                     "';};};";
60472         }else{
60473             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60474             body.push(tpl.body.replace(/(\r\n|\n)/g,
60475                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60476             body.push("'].join('');};};");
60477             body = body.join('');
60478         }
60479         
60480         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60481        
60482         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60483         eval(body);
60484         
60485         return this;
60486     },
60487
60488     applyTemplate : function(values){
60489         return this.master.compiled.call(this, values, {});
60490         //var s = this.subs;
60491     },
60492
60493     apply : function(){
60494         return this.applyTemplate.apply(this, arguments);
60495     }
60496
60497  });
60498
60499 Roo.XTemplate.from = function(el){
60500     el = Roo.getDom(el);
60501     return new Roo.XTemplate(el.value || el.innerHTML);
60502 };